我正在阅读某人制作的 bash 脚本,我注意到作者没有使用 eval 来评估变量作为命令
作者使用
bash -c "$1"
代替
eval "$1"
我认为使用 eval 是首选方法,而且无论如何它可能更快。真的吗?
两者之间有什么实际区别吗?两者之间有哪些显着差异?
答案1
eval "$1"
执行当前脚本中的命令。它可以设置和使用当前脚本中的shell变量,设置当前脚本的环境变量,设置和使用当前脚本中的函数,设置当前脚本的当前目录、umask、limits和其他属性等等。bash -c "$1"
在完全独立的脚本中执行命令,该脚本继承环境变量、文件描述符和其他进程环境(但不传回任何更改),但不继承内部 shell 设置(shell 变量、函数、选项、陷阱等)。
还有另一种方法(eval "$1")
,它在子 shell 中执行命令:它继承调用脚本的所有内容,但不传回任何更改。
例如,假设变量dir
未导出且$1
为cd "$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"
但是用eval
or sh
:
var='echo "this is var"' ; eval "$var" ; sh -c "$var"
输出:
this is var
this is var
当我们使用eval
orsh
时,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/echo
为echo
进行bash -c
测试,花费了1.28s
.差不多是这样。然而,eval
运行可执行文件的速度更快。这里的主要区别是eval
不启动一个新的 shell(它在当前的 shell 中执行命令),而是bash -c
启动一个新的 shell,然后在新的 shell 中执行命令。启动一个新的 shell 需要时间,这就是为什么bash -c
它比eval
.