变量作为命令; eval 与 bash -c

变量作为命令; eval 与 bash -c

我正在阅读某人制作的 bash 脚本,我注意到作者没有使用 eval 来评估变量作为命令
作者使用

bash -c "$1"

代替

eval "$1"

我认为使用 eval 是首选方法,而且无论如何它可能更快。真的吗?
两者之间有什么实际区别吗?两者之间有哪些显着差异?

答案1

eval "$1"执行当前脚本中的命令。它可以设置和使用当前脚本中的shell变量,设置当前脚本的环境变量,设置和使用当前脚本中的函数,设置当前脚本的当前目录、umask、limits和其他属性等等。bash -c "$1"在完全独立的脚本中执行命令,该脚本继承环境变量、文件描述符和其他进程环境(但不传回任何更改),但不继承内部 shell 设置(shell 变量、函数、选项、陷阱等)。

还有另一种方法(eval "$1"),它在子 shell 中执行命令:它继承调用脚本的所有内容,但不传回任何更改。

例如,假设变量dir未导出且$1cd "$foo"; ls,则:

  • cd /starting/directory; foo=/somewhere/else; eval "$1"; pwd列出 的内容/somewhere/else并打印/somewhere/else
  • cd /starting/directory; foo=/somewhere/else; (eval "$1"); pwd列出 的内容/somewhere/else并打印/starting/directory
  • cd /starting/directory; foo=/somewhere/else; bash -c "$1"; pwd列出 的内容/starting/directory(因为cd ""不会更改当前目录)并打印/starting/directory.

答案2

之间最重要的区别

bash -c "$1" 

eval "$1"

前者在子 shell 中运行,而后者则不在子 shell 中运行。所以:

set -- 'var=something' 
bash -c "$1"
echo "$var"

输出:

#there doesn't seem to be anything here
set -- 'var=something' 
eval "$1"
echo "$var"

输出:

something

不过,我不知道为什么有人会bash以这种方式使用可执行文件。如果必须调用它,请使用 POSIX 保证的内置sh.或者(subshell eval)如果您想保护您的环境。

.dot就我个人而言,我更喜欢 shell 。

printf 'var=something%d ; echo "$var"\n' `seq 1 5` | . /dev/fd/0

输出

something1
something2
something3
something4
something5

但你真的需要它吗?

实际上,使用其中任何一个的唯一原因是您的变量实际上分配或评估另一个变量,或者分词对输出很重要。

例如:

var='echo this is var' ; $var

输出:

this is var

这是可行的,但只是因为echo不关心它的参数计数。

var='echo "this is var"' ; $var

输出:

"this is var"

看?双引号的出现是因为 shell 扩展的结果$var没有被评估quote-removal

var='printf %s\\n "this is var"' ; $var

输出:

"this
is
var"

但是用evalor sh

    var='echo "this is var"' ; eval "$var" ; sh -c "$var"

输出:

this is var
this is var

当我们使用evalorsh时,shell 对扩展结果进行第二次传递,并将它们也作为潜在命令进行评估,因此引号会有所不同。您还可以这样做:

. <<VAR /dev/fd/0
    ${var:=echo "this is var"}
#END
VAR

输出

this is var

答案3

我做了一个快速测试:

time bash -c 'for i in {1..10000}; do bash -c "/bin/echo hi"; done'
time bash -c 'for i in {1..10000}; eval "/bin/echo hi"; done'

(是的,我知道,我使用 bash -c 来执行循环,但这应该没有什么区别)。

结果:

eval    : 1.17s
bash -c : 7.15s

所以eval速度更快。从手册页eval

eval 实用程序应通过将参数连接在一起并用字符分隔每个参数来构造命令。构造的命令应由 shell 读取并执行。

bash -c当然,在 bash shell 中执行命令。需要注意的是:我使用它是/bin/echo因为echo是一个内置的 shell bash,这意味着不需要启动新进程。替换/bin/echoecho进行bash -c测试,花费了1.28s.差不多是这样。然而,eval运行可执行文件的速度更快。这里的主要区别是eval不启动一个新的 shell(它在当前的 shell 中执行命令),而是bash -c启动一个新的 shell,然后在新的 shell 中执行命令。启动一个新的 shell 需要时间,这就是为什么bash -c它比eval.

相关内容