Bash getopts:为可选标志读取$ OPTARG?

时间:2013-08-24 01:34:31

标签: bash

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

#!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

非常感谢大家。这到目前为止完美无缺。

6 个答案:

答案 0 :(得分:37)

:表示“接受论证”,而非“强制性论证”。也就是说,:后面跟着的选项字符表示标志式选项(无参数),而后跟:的选项字符表示带参数的选项。

因此,你可能想要

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

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

答案 1 :(得分: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必须减1。当然,该选项现在需要有一个参数,这意味着当一个选项缺少一个参数时会找到一个字符。只需使用另一个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

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

答案 2 :(得分:4)

大多数shell getopts很长一段时间都让我烦恼,包括缺乏对可选参数的支持。

但如果您愿意使用“--posix”样式参数,请访问bash argument case for args in $@

答案 3 :(得分:4)

了解bash的{​​{1}}

getopts的{​​{1}}手册页(引用版本4.1手册)说:

  

bash getopts

     shell脚本使用

getopts optstring name来解析位置参数。 optstring 包含   要识别的选项字符;如果一个字符后跟一个   冒号,期望选项有一个参数,应该分开   从它的白色空间。冒号(':')和问号('?')可能不是   用作选项字符。每次调用它时,[args]都会放置下一个   shell变量 name 中的选项,初始化 name (如果它不存在),以及   要处理到变量getopts的下一个参数的索引。 getopts   每次调用shell或shell脚本时,都会初始化为1。当一个   选项需要参数,OPTIND将该参数放入变量中   OPTIND。 shell不会自动重置getopts;它必须是手动的   如果a,在同一个shell调用中多次调用OPTARG之间重置   将使用新的参数集。

     

遇到选项结束时,OPTIND退出并返回值   大于零。 getopts设置为第一个非选项参数的索引,   并且 name 设置为'?'。

     

getopts通常会解析位置参数,但是如果有更多的参数   在 args 中给出,OPTIND会解析这些。

     

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

     

如果看到无效选项,getopts将'?'放入名称,如果不是沉默,   打印错误消息并取消设置OPTERR。如果getopts是静默的,则选项   找到的字符放在OPTARG中,不会打印诊断消息。   如果找不到必需的参数,并且getopts不是静音,则为问号   ('?')放在名称中,未设置OPTARG,并打印诊断消息。如果   getopts是静默的,然后冒号(':')放在 name 中,OPTARG设置为   找到了选项字符。

请注意:

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

  2. 该描述未提及有关可选选项参数的任何内容。

  3. 我假设你的功能类似于:

    OPTARG

    其中标志getoptsscript -ffilename script -f )可选地接受参数。 f的{​​{1}}命令不支持此功能。 POSIX函数-f几乎不支持该表示法。实际上,只有命令行上的最后一个选项才能在POSIX下有一个可选参数。

    有哪些替代方案?

    部分参考Using getopts in bash shell script to get long and short command-line options

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

答案 4 :(得分:4)

对于bash,这是我最喜欢的解析/支持cli args的方法。我使用了getopts,它太令人沮丧,因为它不支持长选项。我确实喜欢它的工作原理 - 特别是对于内置功能。<​​/ p>

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

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

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

干杯=)

答案 5 :(得分:-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""