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

ear*_*rth 24 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""
Run Code Online (Sandbox Code Playgroud)

但是,输出如下:

bash script.sh -a apple -b banana -c cherry -d dfruit -e eggplant -f fig
Run Code Online (Sandbox Code Playgroud)

...输出:

Apple is apple
Banana is banana
Cherry is 
Dfruit is 
Eggplant is 
Fig is
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,可选标志不会像使用$ 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
Run Code Online (Sandbox Code Playgroud)

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

nne*_*neo 42

:意思是"接受论证",而不是"强制性论证".也就是说,未跟随的选项字符:表示标志样式选项(无参数),而后跟的选项字符:表示带参数的选项.

因此,你可能想要

getopts "a:b:c:d:e:f:" opt
Run Code Online (Sandbox Code Playgroud)

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


小智 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
Run Code Online (Sandbox Code Playgroud)

执行此操作的唯一方法是测试选项及其参数是否与脚本位于同一参数中.如果是这样,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
Run Code Online (Sandbox Code Playgroud)

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


Gil*_*ert 6

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

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


Jon*_*ler 5

理解bashgetopts

bash手册页(报价4.1版手册)的getopts说:

getopts optstring name[args]

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

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

getopts 通常解析位置参数,但如果在 ARGSgetopts解析那些代替。

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

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

注意:

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

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

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

script -ffilename
script -f
Run Code Online (Sandbox Code Playgroud)

其中标志f( -f) 可选择接受一个参数。bashgetopts命令不支持此操作。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/


Wil*_*ton 5

对于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
Run Code Online (Sandbox Code Playgroud)

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

我认为可以对其进行修改以支持“-”选项。

干杯=)