getopt、getopts 或手动解析 - 当我想同时支持短选项和长选项时使用什么?

getopt、getopts 或手动解析 - 当我想同时支持短选项和长选项时使用什么?

目前我正在编写一个 Bash 脚本,它有以下要求:

  • 它应该运行在各种 Unix/Linux 平台上
  • 它应该支持短选项和(GNU)长选项

我知道这将getopts是可移植性方面的首选方式,但据我所知它不支持长选项。

getopt支持长选项,但Bash指南强烈建议反对:

切勿使用 getopt(1)。 getopt 无法处理空参数字符串或嵌入空格的参数。请忘记它曾经存在过。

所以,还是有手动解析的选项。这很容易出错,会产生相当多的样板代码,并且我需要自己处理错误(我想getopt(s)自己进行错误处理)。

那么,在这种情况下,首选是什么?

答案1

getoptvsgetopts似乎是一个宗教问题。至于反对意见getoptbash 常见问题解答:

  • getopt无法处理空参数字符串”似乎指的是一个已知问题选修的参数,看起来getopts根本不支持(至少从help getoptsBash 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

相关内容