0

如果我将我的 cat 命令保存到一个字符串中然后我执行它,那么我会得到一个错误

linux# cmd="cat /data/test/test.tx* | grep toto"
linux# eval '$cmd'
cat: |: No such file or directory
cat: grep: No such file or directory
cat: toto: No such file or directory

即使

linux# $cmd
cat: |: No such file or directory
cat: grep: No such file or directory
cat: toto: No such file or directory

我知道

linux# eval "$cmd"

有效,但在我的脚本中我想使用eval '$cmd'

将 cat 命令保存到变量中时如何执行?

现在如果cmd="echo anymessage"那时

linux# eval '$cmd'
linux# $cmd
linux# eval "$cmd"

他们都会工作

4

3 回答 3

6

让我们从一个简单的命令开始,它可以以任何方式工作,但原因不同。

$ cmd="echo anymessage"
$ eval '$cmd'

eval '$cmd'eval处理字符串$cmd。它将它分成单词,这又是一个单词$cmd。它将参数扩展为echo anymessage并执行一轮分词以获得echoanymessage。现在,echo是命令并且anymessage是参数,并且echo命令被执行。

$ eval "$cmd"

这里,eval是处理字符串echo anymessage,因为在执行之前bash对参数进行了扩展。该字符串再次被拆分为单词,并被识别为命令。$cmd evalecho

现在,让我们为cmd.

$ cmd="cat /data/test/test.tx* | grep toto"
$ eval '$cmd'

同样,eval接收文字 string $cmd。这是整个命令行,在分词之后,所以它是一个简单的命令:没有管道,没有重定向等。参数被扩展为单个string cat /data/test/test.tx* | grep toto。接下来,分词产生单词cat/data/test/test.tx*|greptoto。路径名扩展发生在模式上;假设它扩展为单个文件名/data/test/test.txt。您现在有 5 个单词无需执行扩展,因此 shell 识别cat命令位置中的单词,并使用剩余的 4 个单词作为参数运行它。cat找不到名为|grep和的文件时会发生错误toto

最后,让我们使用双引号

$ eval "$cmd"

这一次,eval接收扩展字符串cat /data/test/test.tx* | grep toto作为命令行。分词后,它看到cat, /data/test/test.tx*, |, grep, 和toto。这个多字命令行包含一个管道字符,因此eval将其作为管道处理。与前面的示例相比,eval的单个参数仅包含一个单词$cmd。从这里,您应该能够看到管道是如何按预期执行的。


总之,eval如果它是包含要解析和执行的复杂命令行的单个参数,则需要在参数上使用双引号。

于 2013-08-23T14:36:33.587 回答
1

您需要告诉 shell 正常处理管道,而不是作为cat命令的一部分。一种方法是向它传递一个新的 shell 实例。

sh -c "$cmd"

请注意,这会引入各种引用问题,最好不要尝试将 shell 元命令存储在变量中。

于 2013-08-23T13:12:09.423 回答
0

另请注意,如果文件夹中没有文件,此代码将失败。这是将要抛出的错误:

cat: *: 没有这样的文件或目录

要解决此问题,您可以启用nullglob

shopt -s nullglob
于 2013-08-23T13:14:07.480 回答