如何避免使用 eval set -- 来解析参数?

如何避免使用 eval set -- 来解析参数?

在研究参数解析时巴什脚本使用获取选择,我几乎到处都看到过这种结构的使用:eval set -- "$SOMETHING_PARSED"。例如,这里这里。由于我将在将来发布我的工作脚本以供免费访问,因此我不想为用户留下这样的漏洞。有什么方法可以避免这种情况吗?

描述的是这里这取决于版本获取选择,你可以不用eval。但至少这不是我的情况。获取选择给出一个像这样的字符串:' -j --version= '\''1'\'' --number= '\''2'\'' -- '\''2C_IA/GBCs_out/nohope_2C_IA_off. xml'\'''. &set将其完全分配给$1.

答案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 分隔的命令,则可以通过将其输出读入数组来将任意字符串列表传递给bash4.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³ 之外)仅支持单字母选项。
  • zshzparseopts立方
  • 我的getopts_longPOSIXsh函数

¹ 不在选项名称中,但至少在版本 2.38 中这可能被视为一个错误,尽管只要您不为脚本选项选择愚蠢的名称就应该没问题

² 当前版本yash(截至 2022 年 6 月)只能处理有效文本,因此无论如何都会因无法在当前语言环境中解码为文本的参数而阻塞

³ ksh93getopts和 zsh都不以与 GNU (或)zparseopts相同的方式解析选项。特别是,它们不支持缩写长选项。getopt_long()util-linux getopt

相关内容