getopt,getopts还是手动解析-当我想同时支持长选项和长选项时使用什么?


32

目前,我正在编写具有以下要求的Bash脚本:

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

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

getopt支持长选项,但BashGuide强烈建议不要这样做

永远不要使用getopt(1)。getopt无法处理空参数字符串或带有嵌入式空格的参数。请忘记它曾经存在。

因此,仍然有手动解析的选项。这容易出错,会产生很多样板代码,我需要自己处理错误(我想自己getopt(s)进行错误处理)。

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


Answers:


9

如果必须将它移植到一系列Unices中,则必须坚持使用POSIX sh。在AFAIU中,您别无选择,只能手动滚动参数处理。


25

getoptvs getopts似乎是一个宗教问题。至于反对的论点getopt猛砸常见问题

  • getopt不能处理空的参数字符串”似乎是指一个带有可选参数的已知问题,它看起来getopts根本不支持(至少从阅读help getoptsBash 4.2.24的文章中得知)。来自man getopt

    getopt(3)可以使用具有空的可选参数的可选参数来解析长选项(但是对于短选项则不能这样做)。此getopt(1)将可选参数视为空,就好像它们不存在一样。

我不知道“ getopt无法处理带有嵌入式空格的参数”的来源,但让我们对其进行测试:

  • test.sh:

    #!/usr/bin/env bash
    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
    

看起来像和我交配,但我敢肯定有人会证明我是如何完全误解了这句话。当然,可移植性问题仍然存在。您必须决定值得花多少时间在较旧的Bash或没有Bash的平台上进行投资。我自己的技巧是使用YAGNIKISS指南-仅针对您知道将要使用的特定平台进行开发。随着开发时间达到无穷大,Shell代码的可移植性通常达到100%。


11
OP提到了可移植到许多Unix平台的需求,而getopt此处引用的是Linux特定的。请注意,getopt它不是的一部分bash,甚至不是GNU实用程序,并且在Linux上是util-linux软件包附带的。
斯特凡Chazelas

2
大多数平台都有getopt,只有Linux AFAIK附带了一个支持长选项或参数中的空格的工具。其他的将仅支持System V语法。
斯特凡Chazelas

7
getopt是传统的命令,它早于Linux发行之前就来自SystemV。getopt从未标准化。POSIX,Unix或Linux(LSB)都没有对该getopt命令进行过标准化。getopts在所有三个选项中均指定,但不支持长选项。
斯特凡Chazelas

1
只是增加了这一讨论:这不是传统的方法getopt。这是@StéphaneChazelas指示的linux-utils风格。它具有遗留的选项,这些选项将禁用上述语法,尤其是手册页状态“ GETOPT_COMPATIBLE强制getopt使用摘要中指定的第一种调用格式”。但是,如果您可以期望目标系统安装了此软件包,则完全可以这样做,因为原始的getopt很糟糕,而Bash的getopt非常有限
n.caillou

1
OP的链接声称“ getopt的传统版本无法处理空参数字符串或带有嵌入式空格的参数。” 而且不应使用getopt的util-linux版本没有证据,而且不再准确。快速调查(超过5年后)显示,ArchLinux,SUSE,Ubuntu,RedHat,Fedora和CentOS(以及大多数派生变体)提供的getopt的默认版本均支持可选参数和带空格的参数。
mtalexan '18

10

可以将这个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它可以做什么以及与其他选项解析器的区别。


2

关于此问题的每次讨论都突出显示了手动编写解析代码的选项-只有这样您才能确定其功能和可移植性。我建议您不要编写易于使用的开源代码生成器可以生成和重新生成的代码。使用Argbash,它旨在为您的问题提供确定的答案。它是一个有据可查的代码生成器,可作为命令行应用程序在线Docker映像使用

我建议不要使用bash库,其中一些库使用了bash库getopt(这使得它非常不可移植),并且将巨大的不可读的shell blob与脚本捆绑在一起是很痛苦的。


0

您可以getopt在支持它的系统上使用,而对不支持它的系统使用后备。

例如 pure-getopt,在纯Bash中实现以替代GNU getopt

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.