【问题标题】:getopts ignores all arguments if an unnamed argument precedes named arguments ("./foo unnamed -n named")如果未命名参数位于命名参数之前,getopts 将忽略所有参数 ("./foo unnamed -n named")
【发布时间】:2012-01-01 05:12:45
【问题描述】:

我试图理解为什么 getopts 似乎忽略了所有参数,如果“未命名”参数位于任何命名参数之前。

使用来自http://wiki.bash-hackers.org/howto/getopts_tutorial 的示例,

#!/bin/bash

while getopts ":a" opt; do
  case $opt in
    a)
      echo "-a was triggered!" >&2
      ;;
    \?)
      echo "Invalid option: -$OPTARG" >&2
      ;;
  esac
done

并观察结果:

$ ./opt_test
$ ./opt_test -a
-a was triggered!
$ ./opt_test -a -f
-a was triggered!
Invalid option: -f
$ ./opt_test a -a -f
$ ./opt_test a -a
$ ./opt_test a -f
$ ./opt_test lala -f
$ 

所以在前面加上一个未命名的参数(一个没有破折号的参数)似乎会让getopts 忽略所有参数。

为什么会这样,我该如何解决?我希望我的程序能够捕获此类内容并打印使用屏幕。

【问题讨论】:

    标签: bash command-line options getopts


    【解决方案1】:

    程序遇到第一个非选项参数时停止处理选项是相当标准的行为。这通常正是您想要的。例如,考虑一下:

    ssh someremotehost ls -l
    

    如果ssh 尝试在第一个非选项参数之后处理选项,您将永远无法将参数传递给远程命令。 getopt 支持的另一个标准是选项处理在 -- 参数处显式停止,因此您可以执行以下操作来删除名为 -f 的文件:

    rm -- -f
    

    如果您真的想在命令行的任何位置处理选项,您可以编写自己的选项处理例程。这并不是真的那么困难,您也可以实现对长选项 (--this-is-a-long-option) 的支持。

    【讨论】:

    • 你说的我猜是真的。然而,从用户的角度来看,这可能是常见的结果,但从编程的角度来看,这几乎不是标准行为; getopt(3) 和 getopt_long(3) 的行为非常不同,Perls "GetOptions" 也是如此。无论如何,现在我知道了。感谢您抽出宝贵时间回复!
    • 这实际上是 POSIX 行为。您会注意到,在存在 POSIXLY_CORRECT 环境变量的情况下,getopt() 的行为方式相同。
    【解决方案2】:

    我使用的解决方法是将数组切片传递给 getopts:

    而不是这个:

    while getopts ":a" opt; do
    

    试试这个:

    while getopts ":a" opt ${@:2}; do
    

    这会将第二个到最后一个参数传递给 getopts 以进行解析,并且可以正常工作以忽略第一个参数。

    【讨论】:

      【解决方案3】:

      引用getopts documentation:

      “以下任何一项都应标识选项的结尾:特殊选项“--”,找到不以“-”开头的参数,或遇到错误。”

      【讨论】:

        【解决方案4】:

        要让选项出现在未命名(位置)参数之前,请使用shift 后缀来清理,

        例如有一个没有值的选项:

        while getopts "u" opt; do
        case $opt in
          u) USE_USERNAMES="TRUE"
          ;;
          \?) echo "Invalid option -$OPTARG" >&2
          ;;
        esac
        done
        
        # Clean-up after arguments parsing
        if test "$USE_USERNAMES"; then
          shift 
        fi
        

        或者,有一个值:

        while getopts "s:" OPT; do
        case $OPT in
          s) SUBMISSIONS="$OPTARG"
          ;;
          \?) echo "Invalid option -$OPTARG" >&2
              exit 1
          ;;
        esac
        done
        
        # Clean-up arguments and assign default values AFTER arguments parsing
        if test "$SUBMISSIONS"; then
          shift 2
        else
          SUBMISSIONS="./logins.txt"
        fi
        
        # Move on to unnamed (positional) arguments
        if ! test "$1"; then
          echo "ERROR: Required first argument missing"
          exit 1
        fi
        
        # Use arguments
        echo "SUBMISSIONS: $SUBMISSIONS"
        echo "FIRST ARG: $1"
        

        参考

        【讨论】:

          猜你喜欢
          • 2020-08-28
          • 1970-01-01
          • 2015-10-31
          • 2022-01-04
          • 1970-01-01
          • 1970-01-01
          • 2020-01-23
          • 1970-01-01
          • 2020-05-31
          相关资源
          最近更新 更多