假设你有下面的命令存储在一个variables中:
COMMAND='echo hello'
有什么区别
$ eval "$COMMAND" hello $ bash -c "$COMMAND" hello $ $COMMAND hello
? 为什么最后的版本几乎从来没有使用,如果它更短,(据我所知)做的是完全一样的东西?
第三种形式完全不同于其他两种 – 但是为了理解为什么,当解释一个命令时,我们需要进入操作的顺序,并且看看每种方法在使用时要遵循哪一个。
eval "$string"
eval "$string"
遵循从#1开始的所有上述步骤。 从而:
>()
被处理 $foo
这样的扩展是很荣幸的 sh -c "$string"
…执行与eval
相同的功能,但在作为单独进程启动的新shell中执行 ; 因此,当这个新进程退出时,对变量状态,当前目录等的更改将到期。 (还要注意,新shell可能是支持不同语言的不同解释器;即sh -c "foo"
将不支持与bash
, ksh
, zsh
等相同的语法)。
$string
…从步骤5开始,“分词”。
这是什么意思?
printf '%s\n' "two words"
将因此解析为printf
%s\n
"two
words"
,而不是printf
%s\n
two words
的常见/预期行为)。
;
s, &
s或类似的)不会发生。 从而:
s='echo foo && echo bar' $s
…将发出以下输出:
foo && echo bar
…而不是以下,否则会发生:
foo bar
没有$(foo)
,没有$foo
,没有<(foo)
等
>foo
或2>&1
是通过字符串分割而不是shell指令创建的另一个单词。
$ bash -c "$COMMAND"
这个版本启动一个新的bash解释器,运行命令,然后退出,将控制返回到原来的shell。 例如,你不需要首先运行bash,你可以从tcsh启动一个bash解释器。 您也可以使用bash脚本来创建新环境,或避免污染当前的环境。
编辑:
正如@CharlesDuffy指出的那样以这种方式启动一个新的bash shell将会清除shell变量, 但是环境变量将会被衍生的shell进程继承。
使用eval
会导致shell解析你的命令两次。 在你给出的例子中,直接执行$ COMMAND或者执行一个eval
是等价的,但是在这里查看答案可以更全面的了解eval
是好还是不好。
至少有不同的时候。 考虑以下:
$ cmd="echo \$var" $ var=hello $ $cmd $var $ eval $cmd hello $ bash -c "$cmd" $ var=world bash -c "$cmd" world
其中显示了执行可变扩展的不同点。 如果我们先set -x
就更加清楚了
$ set -x $ $cmd + echo '$var' $var $ eval $cmd + eval echo '$var' ++ echo hello hello $ bash -c "$cmd" + bash -c 'echo $var' $ var=world bash -c "$cmd" + var=world + bash -c 'echo $var' world
我们可以在这里看到查尔斯·达菲在他杰出的答案中谈到的很多东西。 例如,试图直接执行变量会打印$var
因为参数扩展和那些先前的步骤已经完成了,所以我们没有得到var
的值,就像我们用eval
。
bash -c
选项只能从父shell中继承export
ed变量,因为我没有导出var
所以它不可用于新的shell。