更新:有些人说应该-永远-不要使用eval。我不同意。我认为,如果可以将腐败的输入传递给,则会产生风险eval
。但是,在很多情况下这都不是风险,因此在任何情况下都应该了解如何使用eval是值得的。这个stackoverflow答案解释了评估的风险以及评估的替代方法。最终,由用户来确定eval是否/何时安全有效地使用。
bash eval
语句允许您执行bash脚本计算或获取的代码行。
也许最直接的示例是bash程序,该程序将另一个bash脚本作为文本文件打开,读取文本的每一行,eval
然后按顺序执行它们。基本上source
,这与bash 语句的行为相同,除非需要对导入脚本的内容执行某种转换(例如,过滤或替换),否则将使用bash 语句。
我很少需要它eval
,但是我发现读取或写入其名称包含在分配给其他变量的字符串中的变量很有用。例如,对变量集执行操作,同时使代码占用空间较小,并避免冗余。
eval
在概念上很简单。但是,bash语言的严格语法以及bash解释器的解析顺序可能会发生细微差别,并使其eval
显得晦涩难懂且难以使用或理解。这里是要领:
传递给的参数是在运行时计算eval
的字符串表达式。eval
将执行其参数的最终解析结果,作为脚本中的实际代码行。
语法和解析顺序很严格。如果结果不是可执行的bash代码行,则在脚本范围内,该程序将在eval
尝试执行垃圾时在语句上崩溃。
测试时,您可以将eval
语句替换为echo
并查看显示的内容。如果在当前上下文中是合法代码,则eval
可以正常运行。
以下示例可能有助于阐明eval的工作原理...
范例1:
eval
“正常”代码前面的语句是NOP
$ eval a=b
$ eval echo $a
b
在上面的示例中,第一个eval
语句没有目的,可以删除。eval
第一行是没有意义的,因为代码没有动态方面,也就是说,它已经被解析为bash代码的最后几行,因此它与bash脚本中的常规代码声明相同。第二eval
也是毫无意义的,因为尽管有一个解析步骤转换$a
为等效的文字字符串,但没有间接性(例如,没有通过实际 bash名词或bash持有的脚本变量的字符串值进行引用),因此它的行为相同作为没有eval
前缀的代码行。
范例2:
使用作为字符串值传递的变量名称执行变量分配。
$ key="mykey"
$ val="myval"
$ eval $key=$val
$ echo $mykey
myval
如果你要 echo $key=$val
,输出将是:
mykey=myval
那是字符串解析的最终结果,它将由eval执行,因此,最后的echo语句的结果...
范例3:
向示例2添加更多间接
$ keyA="keyB"
$ valA="valB"
$ keyB="that"
$ valB="amazing"
$ eval eval \$$keyA=\$$valA
$ echo $that
amazing
上面的代码比前面的示例要复杂一些,它更多地依赖于bash的解析顺序和特性。该eval
行将大致按以下顺序在内部进行解析(请注意,以下语句是伪代码,而不是实际代码,只是为了试图说明该语句如何在内部分解为多个步骤以得出最终结果)。
eval eval \$$keyA=\$$valA # substitution of $keyA and $valA by interpreter
eval eval \$keyB=\$valB # convert '$' + name-strings to real vars by eval
eval $keyB=$valB # substitution of $keyB and $valB by interpreter
eval that=amazing # execute string literal 'that=amazing' by eval
如果假定的解析顺序不能解释评估的作用,则第三个示例可能会更详细地描述解析,以帮助阐明正在发生的事情。
范例4:
发现是否有vars,其名称包含在字符串中的本身是否包含字符串值。
a="User-provided"
b="Another user-provided optional value"
c=""
myvarname_a="a"
myvarname_b="b"
myvarname_c="c"
for varname in "myvarname_a" "myvarname_b" "myvarname_c"; do
eval varval=\$$varname
if [ -z "$varval" ]; then
read -p "$varname? " $varname
fi
done
在第一次迭代中:
varname="myvarname_a"
Bash将参数解析为eval
,并eval
在运行时从字面上看到:
eval varval=\$$myvarname_a
以下伪代码试图说明bash 如何解释上述实代码行,以得出由所执行的最终值eval
。(以下几行是描述性的,而不是确切的bash代码):
1. eval varval="\$" + "$varname" # This substitution resolved in eval statement
2. .................. "$myvarname_a" # $myvarname_a previously resolved by for-loop
3. .................. "a" # ... to this value
4. eval "varval=$a" # This requires one more parsing step
5. eval varval="User-provided" # Final result of parsing (eval executes this)
一旦完成所有解析,结果就是执行的结果,其效果是显而易见的,这表明eval
自身没有什么特别神秘的东西,并且复杂性在于其参数的解析。
varval="User-provided"
上面示例中的其余代码只是简单地测试以查看分配给$ varval的值是否为null,如果是,则提示用户提供一个值。
$($n)
运行$n
。它试图运行1
不存在的命令。