mur*_*uru 11
我知道两个......常见......用例用于eval:
参数处理getopt:
[T]他的实现可以生成引用的输出,它必须再次被shell解释(通常使用eval命令)。
设置SSH 代理:
[T]代理打印所需的 shell 命令(可以生成 sh(1) 或 csh(1) 语法),这些命令可以在调用 shell 中进行评估,例如
eval `ssh-agent -s`对于 Bourne 类型的 shell,例如 sh(1) 或 ksh( 1) 以及eval `ssh-agent -c`csh(1) 和衍生工具。
两种用途可能都有替代方案,但我不会在看到它们中的任何一个时眨眼。
要在没有像 Bash & Co. slicing (ie ${@: -1})这样的扩展的 POSIX shell 中获取最后一个参数,可以使用
eval "v=\${$#}"
Run Code Online (Sandbox Code Playgroud)
$# 不受讨厌的技巧的影响,因为它是 shell 内部的,并且只能包含脚本/函数的参数数量。
我没有想出那个,是Stéphane Chazelas 在评论中。这个答案中还提到了为什么以及何时应该避免使用 eval ?.
使用 bash,因为Brace Expansion 发生在 Shell Parameter Expansion之前:
$ char="F"
$ range=( {A.."$char"} )
$ declare -p range
declare -a range=([0]="{A..F}")
$ eval "range=( {A..$char} )"
$ declare -p range
declare -a range=([0]="A" [1]="B" [2]="C" [3]="D" [4]="E" [5]="F")
Run Code Online (Sandbox Code Playgroud)
我自己的“现实世界”用例的一些示例,在这些用例中,我无法想出更好的替代方案,eval只能巧妙地完成工作。
“条件扩展”用例。在这里,我想仅在$rmsg_pfx具有某些值时才使用重定向:
eval 'printf -- %s%s\\n "$rmsg_pfx" "$line" '"${rmsg_pfx:+>&2}"
Run Code Online (Sandbox Code Playgroud)
我不能没有它,eval因为那么该>&2位将作为参数扩展printf而不是作为它的重定向。
我可以改为复制该行以说明是否$rmsg_pfx为空,但这将是.. 好吧.. 代码重复。
说到重定向,作为一个“间接”用例,我喜欢依赖{varname}>&...重定向语法,我像下面这样模拟 POSIXly:
eval 'printf -- %s%s\\n "$rmsg_pfx" "$line" '"${rmsg_pfx:+>&2}"
Run Code Online (Sandbox Code Playgroud)
以上是关闭 fds,同样我正在做一个类似的间接模拟 fds 的打开。显然$rses_fd0和$rses_fd1是脚本的内部变量,从头到尾完全在它的控制之下。
有时,我不得不eval简单地“保护”旨在针对特定 shell 而不会干扰其他 shell 的 shell 代码片段。
例如,下面的一段代码来自一个可移植的脚本(POSIXly),同时还嵌入了一些特定于 shell 的优化:
# equivalent of bash/ksh `exec {rses_fd0}>&- {rses_fd1}<&-` redirection syntax
eval "exec $rses_fd0>&- $rses_fd1<&-"
Run Code Online (Sandbox Code Playgroud)
dash 只是在词法级别上因未知(但直接)的语法而窒息,即使这种语法从未进入实际的代码路径。
另一个“保护”用例,在不同的意义上。有时我只是不想为保存和恢复目的而发明“不太可能”的名称。例如在下面的情况下,我只想$r保留 的值:
sochars='][ (){}:,!'"'\\"
# NOTE: wrapped in an eval to protect it from dash which croaks over the regex
eval 'o=; while [[ "$s" =~ ([^$sochars]*)([$sochars])(.*) ]]; do
...
done'
Run Code Online (Sandbox Code Playgroud)
我实际上经常使用上面的技巧来保留循环套件的退出状态,同时还进行清理操作,如下所示:
# wrapped in eval just to make sure that $r is not overwritten by (the call chain of) coolf
eval '
coolf "$tmp" || return "$lerrno"'"
return $r
"
Run Code Online (Sandbox Code Playgroud)
或者在与上述类似的情况下,作为“延迟执行”:
done <&3
eval "unset ret vals; exec 3<&-; return $?"
}
Run Code Online (Sandbox Code Playgroud)
请注意,上述两个片段的一个隐含意图是不从函数执行中留下“工件”,尤其是当函数旨在以交互方式运行时。对于后一种情况,我可以改为:
done
# return boolean set by loop while also unsetting it
eval "unset ok; ${ok:-false}"
}
Run Code Online (Sandbox Code Playgroud)
但对我来说看起来很粗糙。
最后,我有一些偶尔的用例,我想/需要在调用的基础上稍微修改或扩展一个函数,也许是为了小的行为改变或支持来自调用者的一些钩子。类似于回调,但采用“内联”方式,这似乎不那么麻烦,尤其是当钩子片段需要访问函数自己的$@参数时。自然地,这样的片段,通过变量馈送,随后eval由函数进行处理,要么完全是静态的/手工制作的,要么是经过大量预控制/消毒的。