目前我正在编写一个 Bash 脚本,它有以下要求:
- 它应该运行在各种 Unix/Linux 平台上
- 它应该支持短选项和(GNU)长选项
我知道这将getopts
是可移植性方面的首选方式,但据我所知它不支持长选项。
getopt
支持长选项,但Bash指南强烈建议反对:
切勿使用 getopt(1)。 getopt 无法处理空参数字符串或嵌入空格的参数。请忘记它曾经存在过。
所以,还是有手动解析的选项。这很容易出错,会产生相当多的样板代码,并且我需要自己处理错误(我想getopt(s)
自己进行错误处理)。
那么,在这种情况下,首选是什么?
答案1
getopt
vsgetopts
似乎是一个宗教问题。至于反对意见getopt
中bash 常见问题解答:
“
getopt
无法处理空参数字符串”似乎指的是一个已知问题选修的参数,看起来getopts
根本不支持(至少从help getopts
Bash 4.2.24 的阅读来看)。从man getopt
:getopt(3) 可以解析具有空可选参数的可选参数的长选项(但不能对短选项执行此操作)。 getopt(1) 将空的可选参数视为不存在。
许多“传统”实现getopt
确实无法解析带有嵌入空格的参数,但 util-linux 中的实现或 Busybox 中包含的实现并非如此:
测试.sh:
#!/usr/bin/env bash getopt -T if [ "$?" != 4 ]; then echo 2>&1 "Wrong version of 'getopt' detected, exiting..." exit 1 fi set -o errexit -o noclobber -o nounset -o pipefail params="$(getopt -o ab:c -l alpha,bravo:,charlie --name "$0" -- "$@")" eval set -- "$params" while true do case "$1" in -a|--alpha) echo alpha shift ;; -b|--bravo) echo "bravo=$2" shift 2 ;; -c|--charlie) echo charlie shift ;; --) shift break ;; *) echo "Not implemented: $1" >&2 exit 1 ;; esac done
跑步:
$ ./test.sh - $ ./test.sh -acb ' whitespace FTW ' alpha charlie bravo= whitespace FTW $ ./test.sh -ab '' -c alpha bravo= charlie $ ./test.sh --alpha --bravo ' whitespace FTW ' --charlie alpha bravo= whitespace FTW charlie
当然,可移植性问题仍然存在;您必须决定在没有该版本的getopt
.我自己的建议是使用亚格尼和吻指南 - 仅针对您知道将要使用的特定平台进行开发。随着开发时间趋于无穷大,Shell 代码的可移植性通常会达到 100%。
答案2
如果它必须可移植到一系列 Unices,那么您必须坚持使用 POSIX sh。 AFAIU 在那里,你别无选择,只能手动滚动参数处理。
答案3
有这个getopts_long编写为 POSIX shell 函数,您可以将其嵌入到脚本中。
请注意,Linux getopt
(来自util-linux
)在不在时可以正常工作传统的模式并支持长选项,但如果您需要移植到其他 Unices,可能不是您的选择。
最新版本的 ksh93 ( getopts
) 和 zsh ( zparseopts
) 内置了对解析长选项的支持,这可能是您的一个选择,因为这些选项可用于大多数 Unices(尽管默认情况下通常不安装)。
另一种选择是使用perl
及其Getopt::Long
模块,这两个选项现在在大多数 Unices 上都可用,可以通过编写整个脚本perl
或仅调用 perl 来解析选项并将提取的信息提供给 shell。就像是:
parsed_ops=$(
perl -MGetopt::Long -le '
@options = (
"foo=s", "bar", "neg!"
);
Getopt::Long::Configure "bundling";
$q="'\''";
GetOptions(@options) or exit 1;
for (map /(\w+)/, @options) {
eval "\$o=\$opt_$_";
$o =~ s/$q/$q\\$q$q/g;
print "opt_$_=$q$o$q"
}' -- "$@"
) || exit
eval "$parsed_ops"
# and then use $opt_foo, $opt_bar...
了解perldoc Getopt::Long
它的功能以及它与其他选项解析器有何不同。
答案4
您可以getopt
在支持它的系统上使用它,并为不支持它的系统使用回退。
例如 pure-getopt
在纯 Bash 中实现,作为 GNU 的直接替代品getopt
。