33

我希望能够在我的脚本中接受强制标志和可选标志。这是我到目前为止所拥有的。

#!bin/bash

while getopts ":a:b:cdef" opt; do
      case $opt in
        a ) APPLE="$OPTARG";;
        b ) BANANA="$OPTARG";;
        c ) CHERRY="$OPTARG";;
        d ) DFRUIT="$OPTARG";;
        e ) EGGPLANT="$OPTARG";;
        f ) FIG="$OPTARG";;
        \?) echo "Invalid option: -"$OPTARG"" >&2
            exit 1;;
        : ) echo "Option -"$OPTARG" requires an argument." >&2
            exit 1;;
      esac
    done
echo "Apple is "$APPLE""
echo "Banana is "$BANANA""
echo "Cherry is "$CHERRY""
echo "Dfruit is "$DFRUIT""
echo "Eggplant is "$EGGPLANT""
echo "Fig is "$FIG""

但是,以下输出:

bash script.sh -a apple -b banana -c cherry -d dfruit -e eggplant -f fig

...输出:

Apple is apple
Banana is banana
Cherry is 
Dfruit is 
Eggplant is 
Fig is

如您所见,可选标志不会像使用必需标志那样使用 $OPTARG 提取参数。有没有办法在不摆脱整洁的“:)”错误处理的情况下读取可选标志上的 $OPTARG?

========================================

编辑:我最终听从了下面吉尔伯特的建议。这是我所做的:

#!/bin/bash

  if [[ "$1" =~ ^((-{1,2})([Hh]$|[Hh][Ee][Ll][Pp])|)$ ]]; then
    print_usage; exit 1
  else
    while [[ $# -gt 0 ]]; do
      opt="$1"
      shift;
      current_arg="$1"
      if [[ "$current_arg" =~ ^-{1,2}.* ]]; then
        echo "WARNING: You may have left an argument blank. Double check your command." 
      fi
      case "$opt" in
        "-a"|"--apple"      ) APPLE="$1"; shift;;
        "-b"|"--banana"     ) BANANA="$1"; shift;;
        "-c"|"--cherry"     ) CHERRY="$1"; shift;;
        "-d"|"--dfruit"     ) DFRUIT="$1"; shift;;
        "-e"|"--eggplant"   ) EGGPLANT="$1"; shift;;
        "-f"|"--fig"        ) FIG="$1"; shift;;
        *                   ) echo "ERROR: Invalid option: \""$opt"\"" >&2
                              exit 1;;
      esac
    done
  fi

  if [[ "$APPLE" == "" || "$BANANA" == "" ]]; then
    echo "ERROR: Options -a and -b require arguments." >&2
    exit 1
  fi

非常感谢大家。到目前为止,这非常有效。

4

6 回答 6

61

:意思是“接受一个论点”,而不是“强制性的论点”。也就是说,后面没有的选项字符:表示标志样式的选项(无参数),而后面的选项字符:表示带有参数的选项。

因此,您可能想要

getopts "a:b:c:d:e:f:" opt

如果您想要“强制”选项(有点矛盾),您可以在参数解析后检查您的强制选项值是否已全部设置。

于 2013-08-24T01:40:10.707 回答
8

这并不容易......据了解,实际上必须需要任何“可选”选项参数getopts。当然,可选参数必须是脚本参数的一部分,与它所附带的选项相同。否则-f带有可选参数的选项-a和带有必需参数的选项可能会混淆:

# Is -a an option or an argument?
./script.sh -f -a foo

# -a is definitely an argument
./script.sh -f-a foo

这样做的唯一方法是测试选项及其参数是否在脚本的同一参数中。如果是这样,OPTARG 是选项的参数。否则,OPTIND 必须减一。Of course, the option is now required to have an argument, meaning a character will be found when an option is missing an argument. 只需使用另一个case来确定是否需要任何选项:

while getopts ":a:b:c:d:e:f:" opt; do
    case $opt in
        a) APPLE="$OPTARG";;
        b) BANANA="$OPTARG";;
        c|d|e|f)
            if test "$OPTARG" = "$(eval echo '$'$((OPTIND - 1)))"; then
                OPTIND=$((OPTIND - 1))
            else
                 case $opt in
                     c) CHERRY="$OPTARG";;
                     d) DFRUIT="$OPTARG";;
                     ...
                esac
            fi ;;
        \?) ... ;;
        :)
             case "$OPTARG" in
                 c|d|e|f) ;; # Ignore missing arguments
                 *) echo "option requires an argument -- $OPTARG" >&2 ;;
            esac ;;
        esac
    done

到目前为止,这对我有用。

于 2013-08-24T02:51:21.870 回答
7

对于 bash,这是我最喜欢的解析/支持 cli 参数的方式。我使用了 getopts,它不支持长选项太令人沮丧了。我确实喜欢它在其他方面的工作方式——尤其是对于内置功能。

usage()
{
    echo "usage: $0 -OPT1 <opt1_arg> -OPT2"
}
while [ "`echo $1 | cut -c1`" = "-" ]
do
    case "$1" in
        -OPT1)
                OPT1_ARGV=$2
                OPT1_BOOL=1
                shift 2
            ;;
        -OPT2)
                OPT2_BOOL=1
                shift 1
            ;;
        *)
                usage
                exit 1
            ;;
esac
done

简短,简单。工程师最好的朋友!

我认为这也可以修改为支持“--”选项......

干杯 =)

于 2014-02-03T18:42:36.187 回答
6

大多数 shell getopts 一直困扰着我,包括缺乏对可选参数的支持。

但是,如果您愿意使用“--posix”样式的参数,请访问$@ 中 args 的 bash 参数案例

于 2013-08-24T01:45:41.910 回答
5

了解bashgetopts

手册页(bash引用 4.1 版手册)getopts说:

getopts optstring name[args]

getoptsshell 脚本使用它来解析位置参数。optstring包含要识别的选项字符;如果一个字符后跟一个冒号,则该选项应该有一个参数,该参数应该用空格与它分开。冒号 (':') 和问号 ('?') 不能用作选项字符。每次调用时,getopts将下一个选项放在 shell 变量name中,如果name不存在则将其初始化,并将下一个要处理的参数的索引放入 variableOPTIND中。OPTIND 每次调用 shell 或 shell 脚本时初始化为 1。当一个选项需要一个参数时,getopts将该参数放入变量中 OPTARG. 外壳不会OPTIND自动重置;getopts如果要使用一组新参数,则必须在同一 shell 调用内的多次调用之间手动重置它。

当遇到选项结束时,getopts以大于零的返回值退出。OPTIND设置为第一个非选项参数的索引,名称设置为“?”。

getopts通常解析位置参数,但如果在args中给出更多参数,则getopts改为解析这些参数。

getopts可以通过两种方式报告错误。如果optstring的第一个字符是冒号,则使用静默错误报告。在正常操作中,当遇到无效选项或缺少选项参数时会打印诊断消息。如果变量OPTERR设置为 0,即使optstring的第一个字符不是冒号,也不会显示错误消息。

如果看到无效选项,则getopts放置“?” 进入name,如果不是静默,则打印一条错误消息并取消设置OPTARG. 如果getopts是静默,则放置找到的选项字符OPTARG并且不打印诊断消息。如果未找到所需的参数,并且不是静默的,则在namegetopts中放置一个问号 ('?') ,取消设置,并打印一条诊断消息。如果 是静默,则在名称中放置一个冒号 (':')并设置为找到的选项字符。OPTARGgetoptsOPTARG

注意:

  1. 选项字符串中的前导冒号getopts进入静默模式;它不会生成任何错误消息。

  2. 该描述没有提到任何关于可选选项参数的内容。

我假设您追求的功能类似于:

script -ffilename
script -f

其中标志f( -f) 可选地接受一个参数。bash' 的getopts命令不支持此功能。POSIX 函数getopt()几乎不支持这种表示法。实际上,在 POSIX 下,只有命令行上的最后一个选项可以有可选参数。

有哪些替代方案?

在某种程度上,请参阅在shell 脚本中使用来获取长短命令行选项getoptsbash

GNU getopt(单数!)程序是一个复杂的野兽,它支持长选项和短选项,并支持长选项的可选参数(并使用 GNU getopt(3)。跟踪它的源代码很有趣;die.net 页面上的链接是错误的;你会在ftp://ftp.kernel.org/pub/linux/utils/util-linux下的子目录中找到它(没有-ng)。我没有在http://www.gnu.org 找到位置/或包含它的http://www.fsf.org/ 。

于 2013-08-24T02:45:40.950 回答
1
#!/bin/bash

while getopts ":a:b:c:d:e:f:" opt; do
      case $opt in
        a ) APPLE="$OPTARG";;
        b ) BANANA="$OPTARG";;
        c ) CHERRY="$OPTARG";;
        d ) DFRUIT="$OPTARG";;
        e ) EGGPLANT="$OPTARG";;
        f ) FIG="$OPTARG";;
        \?) echo "Invalid option: -"$OPTARG"" >&2
            exit 1;;
        : ) echo "Option -"$OPTARG" requires an argument." >&2
            exit 1;;
      esac
    done
echo "Apple is "$APPLE""
echo "Banana is "$BANANA""
echo "Cherry is "$CHERRY""
echo "Dfruit is "$DFRUIT""
echo "Eggplant is "$EGGPLANT""
echo "Fig is "$FIG""
于 2018-06-22T12:47:34.963 回答