作为命令变量;评估vs bash -c


41

我正在阅读某人制作的bash脚本,发现作者没有使用eval将变量作为命令求值

bash -c "$1"

代替

eval "$1"

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


在某些情况下,您可能会一无所获。e='echo foo'; $e效果很好。
丹尼斯

Answers:


40

eval "$1"在当前脚本中执行命令。它可以设置和使用当前脚本中的shell变量,设置当前脚本中的环境变量,设置和使用当前脚本中的函数,设置当前目录,umask,限制和当前脚本中的其他属性,等等。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

谢谢。我不知道(eval“ $ 1”),它与源代码有什么不同吗?
whoami 2014年

1
@whoami (eval "$1")与无关source。这只是一个组合(…)evalsource foo大致等于eval "$(cat foo)"
吉尔(Gilles)'所以

我们必须同时写出答案...
mikeserv

@whoami eval和之间的主要区别.doteval使用参数.dot使用文件。
mikeserv

谢谢你们俩 现在我再次阅读它,我以前的评论似乎有点愚蠢……
whoami 2014年

23

之间最重要的区别

bash -c "$1" 

eval "$1"

是前者运行在子外壳中,而后者却没有。所以:

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

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"

看到?之所以会出现双引号,是因为$var未评估的壳扩展结果quote-removal

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

输出:

"this
is
var"

但是用evalsh

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

输出:

this is var
this is var

当我们使用evalshshell对扩展结果进行第二遍处理并将其评估为潜在命令时,引号也会有所不同。您也可以:

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

输出值

this is var

5

我做了一个快速测试:

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实用程序应通过将参数连接在一起并用字符分隔每个参数来构造命令。构造的命令应由外壳读取并执行。

bash -c当然,在bash shell中执行命令。一个注意事项:我/bin/echo之所以使用它是因为echo内置了一个shell bash,这意味着不需要启动新进程。更换/bin/echoechobash -c试验,花了1.28s。差不多。但是,eval运行可执行文件的速度更快。此处的主要区别在于,eval它不会启动新的外壳程序(它在当前外壳程序中执行命令),而是bash -c启动一个新的外壳程序,然后在新的外壳程序中执行命令。启动新的shell需要时间,这就是为什么bash -c它比慢eval


我认为OP希望比较bash -ceval没有exec
约瑟夫R.14年

@JosephR。糟糕!我会改变的。
PlasmaPower 2014年

1
@JosephR。现在应该修复。此外,我重做测试了一点,bash -c是不是那个坏...
PlasmaPower

3
尽管这是事实,但它错过了在不同环境中执行命令的根本区别。很明显,启动一个新的bash实例会比较慢,这不是一个有趣的发现。
吉尔斯(Gilles)'所以
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.