![如何避免使用 eval set -- 来解析参数?](https://linux22.com/image/207488/%E5%A6%82%E4%BD%95%E9%81%BF%E5%85%8D%E4%BD%BF%E7%94%A8%20eval%20set%20--%20%E6%9D%A5%E8%A7%A3%E6%9E%90%E5%8F%82%E6%95%B0%EF%BC%9F.png)
答案1
getopt
(以非传统方式实现util-linux
或至少以非传统方式使用时)的全部要点busybox
是它输出要评估的 shell 代码。确实在选项参数 1 中引用了 shell 语法中的特殊字符,因此可以安全地在除²getopt
之外的类似 Bourne 的 shell 中使用。yash
使用 POSIXsh
语言,这是命令为 shell 返回任意字符串列表的最简单方法。
getopt
不支持该操作方式的实现(例如getopt
依赖于使用 split+glob 运算符的原始实现)在设计上被破坏,因为它们无法返回任意列表。
eval
如果您出于某种原因不想使用,但zsh
可以选择切换到,则可以使用:
argv=( "${(@XQ)${(z)$(getopt...)}}" ) || exit
其中z
参数扩展标志基于 shell 引用语法(zsh
引用语法,但getopt
仅使用单引号进行引用)进行标记Q
,删除引号并X
在出现语法错误时进行抱怨。
唯一的好处是,如果getopt
被一些输出邪恶代码的邪恶命令替换,它就不会运行,但话又说回来,虽然邪恶的家伙正在更换getopt
,他们不妨把它拿到跑步邪恶的代码本身,而不是输出它供您评估它。
如果您发送 NUL 分隔的命令,则可以通过将其输出读入数组来将任意字符串列表传递给bash
4.4+(而不是):sh
readarray -td '' array < <(cmd) || exit # readarray failed.
wait "$!" || exit # cmd failed
set -- "${array[@]}"
或者与以下相同zsh
:
argv=( "${(0@)$(cmd)}" ) || exit
argv[-1]=() # remove the empty trailing argument caused by the terminating NUL
因此,理论上可以实现getopt
使用这种操作方式的方法。
请注意,这getopt
并不是唯一期望您评估其输出的实用程序,另请参阅:
eval "$(dbus-launch --sh-syntax)"
eval "$(dpkg-architecture -u)"
eval "set $(git rev-parse --sq --prefix "$prefix" -- "$@")"
eval "$(lesspipe)"
eval "$(xz --robot --version)"
eval "$(resize)"
eval "$(dircolors)"
它用于修改 shell 的环境。
另请参阅所有可用于保存当前状态并稍后恢复的内容:
aliases=$(alias -L) # zsh
aliases=$(alias -p) # ksh/bash
options=$(set +o)
locale=$(locale)
要恢复为eval "$aliases"
,eval "$options"
...
getopt
不涉及使用的解析选项的替代方案eval
:
- 标准
getopts
内置程序(除了ksh93
³ 之外)仅支持单字母选项。 zsh
的zparseopts
立方- 我的
getopts_long
POSIXsh
函数
¹ 不在选项名称中,但至少在版本 2.38 中这可能被视为一个错误,尽管只要您不为脚本选项选择愚蠢的名称就应该没问题
² 当前版本yash
(截至 2022 年 6 月)只能处理有效文本,因此无论如何都会因无法在当前语言环境中解码为文本的参数而阻塞
³ ksh93getopts
和 zsh都不以与 GNU (或)zparseopts
相同的方式解析选项。特别是,它们不支持缩写长选项。getopt_long()
util-linux
getopt