更新:有人说不应该 - 永远 - 使用 eval。我不同意。我认为当损坏的输入可以传递给eval
. 但是,在许多常见情况下,这不是风险,因此在任何情况下都值得了解如何使用 eval。这个stackoverflow 答案解释了 eval 的风险和 eval 的替代方案。最终,由用户来确定 eval 是否/何时安全有效地使用。
basheval
语句允许您执行由 bash 脚本计算或获取的代码行。
也许最直接的例子是一个 bash 程序,它打开另一个 bash 脚本作为文本文件,读取每一行文本,然后eval
按顺序执行它们。这本质上与 bash 语句的行为相同source
,除非有必要对导入脚本的内容执行某种转换(例如过滤或替换),否则会使用该语句。
我很少需要,但我发现读取或写入名称包含在分配给其他变量的字符串中的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:
使用作为字符串值传递的 var 名称执行 var 赋值。
$ 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
如果假定的解析顺序不能充分解释 eval 的作用,则第三个示例可能会更详细地描述解析以帮助澄清正在发生的事情。
示例 4:
发现名称包含在字符串中的变量本身是否包含字符串值。
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 的值是否为空,如果是,则提示用户提供一个值。