答案1
答案2
要在没有 Bash & Co. 切片(即 )等扩展的 POSIX shell 中获取最后一个参数${@: -1}
,可以使用
eval "v=\${$#}"
$#
不会受到令人讨厌的技巧的影响,因为它是 shell 内部的并且只能包含脚本/函数的参数数量。
这不是我想出来的,而是史蒂芬·查泽拉斯评论。中也提到了这个答案为什么以及何时应避免使用 eval?。
答案3
我自己的“现实世界”用例的一些例子,在这些例子中,我无法想出更好的替代方案,但eval
只能整齐地完成工作。
“条件扩展”用例。在这里,我只想在$rmsg_pfx
具有某些价值时才使用重定向:
eval 'printf -- %s%s\\n "$rmsg_pfx" "$line" '"${rmsg_pfx:+>&2}"
如果没有,我就无法做到这一点,eval
因为这样该>&2
位将作为参数展开,printf
而不是作为其重定向。
我可以复制该行来说明$rmsg_pfx
是否为空,但这将是.. 好吧.. 代码重复。
说到重定向,作为“间接”用例,我喜欢依赖{varname}>&...
重定向语法,我模拟 POSIXly,如下所示:
# equivalent of bash/ksh `exec {rses_fd0}>&- {rses_fd1}<&-` redirection syntax
eval "exec $rses_fd0>&- $rses_fd1<&-"
上面是关闭 fds 的,同样我正在做一个类似的间接来模拟 fds 的打开。显然$rses_fd0
和$rses_fd1
是脚本的内部变量,从头到尾完全受其控制。
有时,我不得不eval
简单地“保护”旨在针对特定 shell 的 shell 代码片段,同时又不干扰其他 shell。
例如,下面的代码来自一个可移植(POSIXly)的脚本,同时还嵌入了一些特定于 shell 的优化:
sochars='][ (){}:,!'"'\\"
# NOTE: wrapped in an eval to protect it from dash which croaks over the regex
eval 'o=; while [[ "$s" =~ ([^$sochars]*)([$sochars])(.*) ]]; do
...
done'
dash
只是在词法级别上被未知(但直接)的语法所阻塞,即使此类语法从未进入实际的代码路径。
另一个不同意义上的“保护”用例。有时,我只是懒得为保存和恢复目的而发明“不太可能”的名称。例如在下面的情况下,我只想$r
保留 的值:
# 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
"
实际上,我经常使用上面的技巧来保留循环套件的退出状态,同时还执行清理操作,如下所示:
done <&3
eval "unset ret vals; exec 3<&-; return $?"
}
或者在与上述类似的情况下,作为“延迟执行”:
done
# return boolean set by loop while also unsetting it
eval "unset ok; ${ok:-false}"
}
请注意,上面两个代码片段的一个隐含意图是不要在函数执行中留下“工件”,特别是当该函数旨在交互运行时。对于后一种情况我可以这样做:
[ "${ok:-false}" = false ] && { unset ok; return 1; } || { unset ok; return 0; }
但对我来说看起来很粗糙。
最后,我有一些偶尔的用例,我想要/需要在调用的基础上稍微修改或扩展一个函数,也许是为了小的行为改变或支持调用者的一些挂钩。就像回调一样,但以“内联”方式,这看起来不那么麻烦,特别是当钩子片段需要访问函数自己的$@
参数时。当然,这样的片段,通过变量提供,随后eval
由函数编辑,它们本身要么是完全静态/手工制作的,要么是经过严格预先控制/清理的。
答案4
eval
我遇到的 现实生活中的用法之一是用于Fluxbox.startfluxbox.dbus.diff.gz
在 Slackware 上。它看起来像这样:
# Start DBUS session bus:
if [ -z "$DBUS_SESSION_BUS_ADDRESS" ]; then
eval $(dbus-launch --sh-syntax --exit-with-session)
fi
尽管这种用法eval
尚未经过数百万人的测试(Slackware 并不常见),但它确实可以完成这项工作。尽管如此,我还是会尽力避免eval
在我的 shell 脚本中出现这种情况。如果我感觉我需要它,例如实现数组或执行变量间接寻址,我会切换到 Bash,如果我仍然觉得我需要它,我会重新考虑脚本设计或切换到完全不同的语言。