在 Mac OSX 附带版本的联机帮助页中getopt
,给出了一个使用构造 的示例args=$(getopt optstring $*); set -- $args
。这里做什么set -- $args
?
此外,考虑函数和测试字符串
f () {
args=$(getopt o: $*)
set -- $args
for i; do
echo $i
done
}
f -o 123 xyz
在 Bash 3.2 和 4.3 中,这会产生
-o
123
--
xyz
但在 Zsh 5.1 中,它产生
-o 123 -- xyz
造成差异的原因是什么?是因为 Zsh 不同的分词行为,还是其他原因?我尝试了引用和参数扩展标志的不同组合,但无法真正弄清楚任一 shell 中到底发生了什么。
答案1
这是糟糕的代码。getopt
几乎无法使用。请使用getopts
内置的 shell(它有两个优点:它可以工作,并且可以在所有 POSIX 平台上使用)。
所做args=$(getopt optstring $*); set -- $args
的是:
- 在空格处分割每个命令行参数,并将其替换为以空格分隔的单词列表。
- 获取每个结果单词并将其解释为通配符模式。如果该模式至少匹配一个文件,则用匹配文件列表替换该模式。
- 将生成的单词列表传递给
getopt
命令。 - 将命令的输出存储
getopt
在变量中args
。这是一个由命令行参数组成的字符串,以空格作为分隔符,并重新排序为选项及其参数,然后--
是非选项操作数。 - 在空格处拆分此字符串,将每个单词解释为通配符模式,并用匹配文件列表(如果有)替换该模式。
- 使用结果列表作为位置参数。
在 Bourne/POSIX 风格的 shell 中,$foo
是“split+glob”运算符。您需要双引号来获取变量的值:"$foo"
。但在 zsh 中,工作方式有所不同:它扩展为except 当为空时$foo
的值。所以在 zsh 中你会得到一个位置参数。foo
foo
可以通过编写 来避免第一次使用 split+glob 运算符getopt optstring "$@"
:这会将位置参数精确地传递给getopt
。但是在解析 的输出时getopt
,拆分是不可避免的(不过您可以禁用通配符),因为getopt
使用空格来分隔参数。问题在于getopt
输出不明确:无法区分参数中的空格和getopt
作为分隔符添加的空格。
执行此操作的正确方法是使用getopts
.它坚固且便携。
while getopts optstring OPTLET; do
# We got the option -$OPTLET
case $OPTLET in
…
esac
done
shift $((OPTIND-1))
# Now the positional parameters are just the non-option operands
¹至少是 BSD 版本。 GNU 版本添加了使其可用的功能。
答案2
set -- $args
根据 的内容设置位置参数$args
。现在您将获得zsh
与其他 POSIX shell之间的不同行为。
因为zsh
不执行场分裂默认情况下,你会得到一个字符串,它是 的内容$args
。您必须显式调用 splitting 以获得与bash
(以及其他 POSIX shell)相同的行为:
set -- ${=args}
bash
Field Splitting
对 的内容执行$args
,产生四个字符串。您可以检查$#
以了解 后的位置参数的数量set -- $args
。
请注意,如果是bash
,您set -f
还应该添加以关闭通配符。