在bash shell脚本中使用getopts来获取长命令和短命令行选项

我希望使用我的shell脚本调用多种形式的命令行选项。

我知道可以使用getopts ,但就像在Perl中一样,我还没有能够对shell执行相同的操作。

关于如何完成这些任何想法,以便我可以使用如下选项:

./shell.sh --copyfile abc.pl /tmp/
./shell.sh -c abc.pl /tmp/

在上面,这两个命令对我的shell来说意味着相同的东西,但是使用getopts ,我还没有能够实现这些?


bash getopts builtin不支持带有双点划线前缀的长选项名称。 它只支持单字符选项。

有一个shell工具getopt是另一个程序,而不是bash内建的。 getopt(3)的GNU实现(由Linux上的命令行getopt(1)使用)支持解析长选项。

但是getopt的BSD实现(例如在Mac OS X上)不支持长选项。


其他一些答案显示了使用bash内建getopts来模拟长选项的解决方案。 这个解决方案实际上是一个简短的选项,其特征是“ - ”。 所以你得到“ - ”作为国旗。 接下来的任何事情都会变成OPTARG,然后用嵌套的case测试OPTARG。

这很聪明,但它带有警告:

  • getopts无法执行opt规范。 如果用户提供无效选项,它不会返回错误。 您在解析OPTARG时必须进行自己的错误检查。
  • OPTARG用于长选项名称,这会在长选项本身有参数时使用复杂。 你最终不得不将自己编码为另一个案例。
  • 因此尽管可以编写更多的代码来解决长期选项缺乏支持的问题,但这还有很多工作要做,并且部分失败了使用getopt分析器来简化代码的目的。


    getoptgetopts是不同的动物,人们似乎对他们的工作有些误解。 getopts是一个内置的命令bash处理在一个循环的命令行选项,并依次在每个发现选项和值赋给内置变量,这样你就可以进一步处理它们。 然而, getopt是一个外部实用程序,它并不实际处理您的选项,例如bash getopts ,Perl Getopt模块或Python optparse / argparse模块。 getopt所做的就是规范化传入的选项 - 即将它们转换为更标准的形式,以便shell脚本更容易处理它们。 例如, getopt的应用程序可能会转换以下内容:

    myscript -ab infile.txt -ooutfile.txt
    

    进入这个:

    myscript -a -b -o outfile.txt infile.txt
    

    你必须自己做实际的处理。 如果您对可以指定选项的方式进行各种限制,则根本不必使用getopt

  • 每个参数只能提供一个选项;
  • 所有选项都在任何位置参数之前(即非选项参数);
  • 对于具有值的选项(例如-o以上),该值必须作为单独的参数(在空格之后)。
  • 为什么使用getopt代替getopts ? 基本的原因是只有GNU getopt支持长名称的命令行选项。(GNU getopt是Linux上的默认设置,Mac OS X和FreeBSD提供了一个基本的和不太有用的getopt ,但是GNU版本可以安装;见下文)。

    例如,下面是一个使用GNU getopt的例子,它来自我的一个名为javawrap的脚本:

    # NOTE: This requires GNU getopt.  On Mac OS X and FreeBSD, you have to install this
    # separately; see below.
    TEMP=`getopt -o vdm: --long verbose,debug,memory:,debugfile:,minheap:,maxheap: 
                 -n 'javawrap' -- "$@"`
    
    if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
    
    # Note the quotes around `$TEMP': they are essential!
    eval set -- "$TEMP"
    
    VERBOSE=false
    DEBUG=false
    MEMORY=
    DEBUGFILE=
    JAVA_MISC_OPT=
    while true; do
      case "$1" in
        -v | --verbose ) VERBOSE=true; shift ;;
        -d | --debug ) DEBUG=true; shift ;;
        -m | --memory ) MEMORY="$2"; shift 2 ;;
        --debugfile ) DEBUGFILE="$2"; shift 2 ;;
        --minheap )
          JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MinHeapFreeRatio=$2"; shift 2 ;;
        --maxheap )
          JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MaxHeapFreeRatio=$2"; shift 2 ;;
        -- ) shift; break ;;
        * ) break ;;
      esac
    done
    

    这可以让你指定像--verbose -dm4096 --minh=20 --maxhe 40 --debugfi="/Users/John Johnson/debug.txt"或类似的选项。 调用getopt的效果是将选项规范化为--verbose -d -m 4096 --minheap 20 --maxheap 40 --debugfile "/Users/John Johnson/debug.txt"以便您可以更轻松地处理他们。 引用"$1""$2"非常重要,因为它可以确保正确处理带有空格的参数。

    如果您删除前9行(通过eval set行的所有内容),代码仍然可以工作! 但是,您的代码在接受什么样的选项时会更加挑剔:特别是,您必须以上述“规范”形式指定所有选项。 但是,通过使用getopt ,您可以将单字母选项分组,使用较短的不含歧义的长选项形式,可以使用--file foo.txt--file=foo.txt样式,使用-m 4096-m4096风格,以任意顺序混合选项和非选项等。如果发现无法识别或含糊不清的选项, getopt也会输出错误消息。

    注意:实际上有两个完全不同的getopt版本,基本的getopt和GNU getopt ,具有不同的功能和不同的调用约定getopt基本的getopt很破碎:它不仅处理长选项,而且甚至无法处理嵌入空格或空参数中的空格,而getopts确实是这样做的。 上面的代码在基本的getopt不起作用。 GNU getopt默认安装在Linux上,但在Mac OS X和FreeBSD上需要单独安装。 在Mac OS X上,安装MacPorts(http://www.macports.org),然后执行sudo port install getopt以安装GNU getopt (通常getopt /opt/local/bin ),并确保/opt/local/bin位于/usr/bin之前的shell路径中。 在FreeBSD上,安装misc/getopt

    修改您自己程序的示例代码的快速指南:在开头的几行中,除了调用getopt那行外,所有代码都应该保持不变。 您应该在-n之后更改程序名称,在-o之后指定短的选项,以及--long之后的长选项。 将冒号放在具有价值的选项之后。

    最后,如果你看到刚刚set代码而不是eval set ,那么它就是为BSD getopt编写的。 您应该将其更改为使用eval set样式,这对于两个版本的getopt都可以正常工作,而普通set不适用于GNU getopt

    1Actually, getoptsksh93支持长命名的选项,但这个壳不作为经常bash 。 在zsh ,使用zparseopts来获得这个功能。

    2在技术上,“GNU getopt ”是一个误用词; 这个版本实际上是为Linux而不是GNU项目编写的。 但是,它遵循所有GNU约定,并且术语“GNU getopt ”通常被使用(例如在FreeBSD上)。


    Bash内建的getopts函数可以用来解析长选项,方法是将一个破折号字符和一个冒号放入optspec中:

    #!/usr/bin/env bash 
    optspec=":hv-:"
    while getopts "$optspec" optchar; do
        case "${optchar}" in
            -)
                case "${OPTARG}" in
                    loglevel)
                        val="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
                        echo "Parsing option: '--${OPTARG}', value: '${val}'" >&2;
                        ;;
                    loglevel=*)
                        val=${OPTARG#*=}
                        opt=${OPTARG%=$val}
                        echo "Parsing option: '--${opt}', value: '${val}'" >&2
                        ;;
                    *)
                        if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
                            echo "Unknown option --${OPTARG}" >&2
                        fi
                        ;;
                esac;;
            h)
                echo "usage: $0 [-v] [--loglevel[=]<value>]" >&2
                exit 2
                ;;
            v)
                echo "Parsing option: '-${optchar}'" >&2
                ;;
            *)
                if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then
                    echo "Non-option argument: '-${OPTARG}'" >&2
                fi
                ;;
        esac
    done
    

    在当前工作目录中复制到可执行文件名称= getopts_test.sh之后,可以生成类似的输出

    $ ./getopts_test.sh
    $ ./getopts_test.sh -f
    Non-option argument: '-f'
    $ ./getopts_test.sh -h
    usage: code/getopts_test.sh [-v] [--loglevel[=]<value>]
    $ ./getopts_test.sh --help
    $ ./getopts_test.sh -v
    Parsing option: '-v'
    $ ./getopts_test.sh --very-bad
    $ ./getopts_test.sh --loglevel
    Parsing option: '--loglevel', value: ''
    $ ./getopts_test.sh --loglevel 11
    Parsing option: '--loglevel', value: '11'
    $ ./getopts_test.sh --loglevel=11
    Parsing option: '--loglevel', value: '11'
    

    显然,getopts不会为长选项执行OPTERR检查和选项参数分析。 上面的脚本片段显示了这可以如何手动完成。 基本原理也适用于Debian Almquist shell(“dash”)。 请注意特殊情况:

    getopts -- "-:"  ## without the option terminator "-- " bash complains about "-:"
    getopts "-:"     ## this works in the Debian Almquist shell ("dash")
    

    请注意,正如http://mywiki.wooledge.org/BashFAQ上的GreyCat指出的那样,这个技巧利用了允许选项参数(即“-f filename”中的文件名)的shell的非标准行为。连接到选项(如“-ffilename”)。 POSIX标准说明它们之间必须有一个空格,在“ - longoption”的情况下将终止选项解析并将所有longoption变为非选项参数。

    链接地址: http://www.djcxy.com/p/97097.html

    上一篇: Using getopts in bash shell script to get long and short command line options

    下一篇: GPU Monitor is missing on android studio 3.0