假设您将以下命令存储在变量中:
COMMAND='echo hello'
之间的区别是什么
$ eval "$COMMAND"
hello
$ bash -c "$COMMAND"
hello
$ $COMMAND
hello
?为什么最后一个版本几乎从不使用,如果它更短并且(据我所知)完全相同的事情?
答案 0 :(得分:11)
第三种形式与其他两种形式完全不同 - 但要理解为什么,我们需要在解释命令时按bash进行操作的顺序,并查看每种方法进入时遵循哪些操作使用
eval "$string"
eval "$string"
遵循从#1开始的所有上述步骤。因此:
>()
等特殊操作符$foo
等扩展sh -c "$string"
...执行与eval
相同,但在作为单独进程启动的新shell中;因此,当这个新进程退出时,对变量状态,当前目录等的更改将到期。 (另请注意,新shell可能是支持不同语言的不同解释器;即。sh -c "foo"
将不支持bash
,ksh
,zsh
的相同语法等等。)。
$string
...从第5步开始,“Word Splitting”。
这是什么意思?
printf '%s\n' "two words"
将解析为printf
%s\n
"two
words"
,而不是printf
{{1}的通常/预期行为} %s\n
(shell引用了引号)。
two words
,;
或类似命令)。因此:
&
...将发出以下输出:
s='echo foo && echo bar'
$s
...而不是以下,否则将是预期的:
foo && echo bar
不是foo
bar
,没有$(foo)
,没有$foo
等等。
<(foo)
或>foo
只是字符串拆分创建的另一个单词,而不是shell指令。
答案 1 :(得分:1)
至少有些时候它们不同。请考虑以下事项:
$ 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
,因为参数扩展和前面的步骤已经完成,所以我们没有像{{1}那样得到var
的值}}
eval
选项仅从父shell继承bash -c
ed变量,因为我没有导出export
,所以它不适用于新shell。
答案 2 :(得分:1)
$ bash -c "$COMMAND"
此版本启动一个新的bash解释器,运行命令,然后退出,将控制权返回给原始shell。你不需要首先运行bash来做到这一点,你可以从tcsh开始一个bash解释器,例如。您也可以从bash脚本执行此操作,以从新环境开始,或者避免污染当前环境。
修改强>
正如@CharlesDuffy指出以这种方式启动一个新的bash shell将清除shell变量,但环境变量将由生成的shell进程继承。
使用eval
会导致shell解析您的命令两次。在您提供的示例中,直接执行$ COMMAND或执行eval
是等效的,但请查看答案here以更全面地了解eval
的优点(或坏的)。