更新:有人说不应该 - 永远 - 使用 eval。我不同意。我认为当损坏的输入可以传递给eval 时,风险就会出现。但是,在许多常见情况下,这不是风险,因此在任何情况下都值得了解如何使用 eval。这个stackoverflow answer 解释了评估的风险和评估的替代方案。最终由用户决定是否/何时使用 eval 安全有效。
bash eval 语句允许您执行由 bash 脚本计算或获取的代码行。
也许最直接的例子是一个 bash 程序,它打开另一个 bash 脚本作为文本文件,读取每一行文本,并使用eval 按顺序执行它们。这与 bash source 语句的行为基本相同,除非有必要对导入脚本的内容执行某种转换(例如过滤或替换)。
我很少需要eval,但我发现读取或写入其名称 包含在分配给其他变量的字符串中的变量很有用。例如,对变量集执行操作,同时保持较小的代码占用空间并避免冗余。
eval 在概念上很简单。但是,bash 语言的严格语法和 bash 解释器的解析顺序可能会很微妙,使eval 显得晦涩难懂,难以使用或理解。以下是要点:
传递给eval 的参数是一个在运行时计算的字符串表达式。 eval 将在您的脚本中将其参数的最终解析结果作为实际行代码执行。
语法和解析顺序非常严格。如果结果不是可执行的 bash 代码行,在您的脚本范围内,程序将在尝试执行垃圾时在 eval 语句上崩溃。
测试时,您可以将 eval 语句替换为 echo 并查看显示的内容。如果它是当前上下文中的合法代码,则通过eval 运行它会起作用。
以下示例可能有助于阐明 eval 的工作原理...
示例 1:
eval'normal' 代码前面的语句是 NOP
$ eval a=b
$ eval echo $a
b
在上面的例子中,第一个eval 语句没有任何用途,可以去掉。 eval 在第一行中毫无意义,因为代码没有动态方面,即它已经解析为 bash 代码的最后几行,因此它与 bash 脚本中的正常代码语句相同。第二个eval 也毫无意义,因为尽管有一个解析步骤将$a 转换为其等效的文字字符串,但没有间接性(例如,没有通过 actual 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:
发现 names 包含在字符串中的 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如何解释上面的real行strong> 代码,得到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,如果是,则提示用户提供一个值。