bash 使用 getopts 处理长短命令行选项

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/402377/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 17:52:17  来源:igfitidea点击:

Using getopts to process long and short command line options

bashcommand-line-argumentsgetoptgetopts

提问by gagneet

I wish to have long and short forms of command line options invoked using my shell script.

我希望使用我的 shell 脚本调用长形式和短形式的命令行选项。

I know that getoptscan be used, but like in Perl, I have not been able to do the same with shell.

我知道getopts可以使用,但就像在 Perl 中一样,我无法用 shell 做同样的事情。

Any ideas on how this can be done, so that I can use options like:

关于如何做到这一点的任何想法,以便我可以使用以下选项:

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

In the above, both the commands mean the same thing to my shell, but using getopts, I have not been able to implement these?

在上面,这两个命令对我的 shell 意味着相同的事情,但是使用getopts,我无法实现这些?

采纳答案by Bill Karwin

There are three implementations that may be considered:

可以考虑三种实现:

  • Bash builtin getopts. This does not support long option names with the double-dash prefix. It only supports single-character options.

  • BSD UNIX implementation of standalone getoptcommand (which is what MacOS uses). This does not support long options either.

  • GNU implementation of standalone getopt. GNU getopt(3)(used by the command-line getopt(1)on Linux) supports parsing long options.

  • Bash 内置getopts. 这不支持带有双破折号前缀的长选项名称。它仅支持单字符选项。

  • 独立getopt命令的BSD UNIX 实现(这是 MacOS 使用的)。这也不支持长选项。

  • 独立getopt. GNU getopt(3)(由getopt(1)Linux 上的命令行使用)支持解析长选项。



Some other answers show a solution for using the bash builtin getoptsto mimic long options. That solution actually makes a short option whose character is "-". So you get "--" as the flag. Then anything following that becomes OPTARG, and you test the OPTARG with a nested case.

其他一些答案显示了使用 bash 内置getopts来模拟长选项的解决方案。该解决方案实际上是一个简短的选项,其字符为“-”。所以你得到“--”作为标志。然后接下来的任何内容都变成 OPTARG,然后您使用嵌套的case.

This is clever, but it comes with caveats:

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

  • getoptscan't enforce the opt spec. It can't return errors if the user supplies an invalid option. You have to do your own error-checking as you parse OPTARG.
  • OPTARG is used for the long option name, which complicates usage when your long option itself has an argument. You end up having to code that yourself as an additional case.
  • getopts无法强制执行 opt 规范。如果用户提供了无效的选项,它就不会返回错误。在解析 OPTARG 时,您必须自己进行错误检查。
  • OPTARG 用于长选项名称,当您的长选项本身具有参数时,这会使使用复杂化。您最终不得不自己将其编码为附加案例。

So while it is possible to write more code to work around the lack of support for long options, this is a lot more work and partially defeats the purpose of using a getopt parser to simplify your code.

因此,虽然可以编写更多代码来解决对长选项缺乏支持的问题,但这需要做更多的工作,并且在一定程度上违背了使用 getopt 解析器来简化代码的目的。

回答by Urban Vagabond

getoptand getoptsare different beasts, and people seem to have a bit of misunderstanding of what they do. getoptsis a built-in command to bashto process command-line options in a loop and assign each found option and value in turn to built-in variables, so you can further process them. getopt, however, is an external utility program, and it doesn't actually process your options for youthe way that e.g. bash getopts, the Perl Getoptmodule or the Python optparse/argparsemodules do. All that getoptdoes is canonicalize the options that are passed in — i.e. convert them to a more standard form, so that it's easier for a shell script to process them. For example, an application of getoptmight convert the following:

getopt并且getopts是不同的野兽,人们似乎对他们的工作有一些误解。 getopts是一个内置命令,bash用于在循环中处理命令行选项并将每个找到的选项和值依次分配给内置变量,以便您可以进一步处理它们。 getopt但是,它是一个外部实用程序,它实际上并不像bash getopts、PerlGetopt模块或 Python optparse/argparse模块那样为您处理选项。所有这一切getopt确实是。规范化所传递的选项-即它们转换为更规范的形式,因此,它是一个shell脚本来处理它们更容易。例如,应用程序getopt可能会转换以下内容:

myscript -ab infile.txt -ooutfile.txt

into this:

进入这个:

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

You have to do the actual processing yourself. You don't have to use getoptat all if you make various restrictions on the way you can specify options:

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

  • only put one option per argument;
  • all options go before any positional parameters (i.e. non-option arguments);
  • for options with values (e.g. -oabove), the value has to go as a separate argument (after a space).
  • 每个参数只放一个选项;
  • 所有选项都在任何位置参数之前(即非选项参数);
  • 对于带有值的选项(例如-o上面),该值必须作为一个单独的参数(在一个空格之后)。

Why use getoptinstead of getopts? The basic reason is that only GNU getoptgives you support for long-named command-line options.1(GNU getoptis the default on Linux. Mac OS X and FreeBSD come with a basic and not-very-useful getopt, but the GNU version can be installed; see below.)

为什么使用getopt而不是getopts?基本原因是只有 GNUgetopt为您提供对长命名命令行选项的支持。1(GNUgetopt是 Linux 上的默认设置。Mac OS X 和 FreeBSD 带有一个基本且不太有用的getopt,但可以安装 GNU 版本;见下文。)

For example, here's an example of using GNU getopt, from a script of mine called javawrap:

例如,这里有一个使用 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 "" in
    -v | --verbose ) VERBOSE=true; shift ;;
    -d | --debug ) DEBUG=true; shift ;;
    -m | --memory ) MEMORY=""; shift 2 ;;
    --debugfile ) DEBUGFILE=""; shift 2 ;;
    --minheap )
      JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MinHeapFreeRatio="; shift 2 ;;
    --maxheap )
      JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MaxHeapFreeRatio="; shift 2 ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

This lets you specify options like --verbose -dm4096 --minh=20 --maxhe 40 --debugfi="/Users/John Johnson/debug.txt"or similar. The effect of the call to getoptis to canonicalize the options to --verbose -d -m 4096 --minheap 20 --maxheap 40 --debugfile "/Users/John Johnson/debug.txt"so that you can more easily process them. The quoting around "$1"and "$2"is important as it ensures that arguments with spaces in them get handled properly.

这使您可以指定类似--verbose -dm4096 --minh=20 --maxhe 40 --debugfi="/Users/John Johnson/debug.txt"或类似的选项。调用 to 的作用getopt是将选项规范化,--verbose -d -m 4096 --minheap 20 --maxheap 40 --debugfile "/Users/John Johnson/debug.txt"以便您可以更轻松地处理它们。该报价各地"$1""$2"非常重要,因为它确保在他们的空间得到妥善处理该参数。

If you delete the first 9 lines (everything up through the eval setline), the code will still work! However, your code will be much pickier in what sorts of options it accepts: In particular, you'll have to specify all options in the "canonical" form described above. With the use of getopt, however, you can group single-letter options, use shorter non-ambiguous forms of long-options, use either the --file foo.txtor --file=foo.txtstyle, use either the -m 4096or -m4096style, mix options and non-options in any order, etc. getoptalso outputs an error message if unrecognized or ambiguous options are found.

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

NOTE: There are actually two totally differentversions of getopt, basic getoptand GNU getopt, with different features and different calling conventions.2Basic getoptis quite broken: Not only does it not handle long options, it also can't even handle embedded spaces inside of arguments or empty arguments, whereas getoptsdoes do this right. The above code will not work in basic getopt. GNU getoptis installed by default on Linux, but on Mac OS X and FreeBSD it needs to be installed separately. On Mac OS X, install MacPorts (http://www.macports.org) and then do sudo port install getoptto install GNU getopt(usually into /opt/local/bin), and make sure that /opt/local/binis in your shell path ahead of /usr/bin. On FreeBSD, install misc/getopt.

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

A quick guide to modifying the example code for your own program: Of the first few lines, all is "boilerplate" that should stay the same, except the line that calls getopt. You should change the program name after -n, specify short options after -o, and long options after --long. Put a colon after options that take a value.

为您自己的程序修改示例代码的快速指南:在前几行中,除了调用getopt. 您应该在 之后更改程序名称,在 之后-n指定短选项,在之后指定-o长选项--long。在取值的选项后放置一个冒号。

Finally, if you see code that has just setinstead of eval set, it was written for BSD getopt. You should change it to use the eval setstyle, which works fine with both versions of getopt, while the plain setdoesn't work right with GNU getopt.

最后,如果您看到的代码只是set代替eval set,则它是为 BSD 编写的getopt。您应该将其更改为使用eval set样式,该样式适用于 的两个版本getopt,而普通样式set不适用于 GNU getopt

1Actually, getoptsin ksh93supports long-named options, but this shell isn't used as often as bash. In zsh, use zparseoptsto get this functionality.

1实际上,getoptsinksh93支持长命名选项,但是这个 shell 不像bash. 在 中zsh,用于zparseopts获取此功能。

2Technically, "GNU getopt" is a misnomer; this version was actually written for Linux rather than the GNU project. However, it follows all the GNU conventions, and the term "GNU getopt" is commonly used (e.g. on FreeBSD).

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

回答by Arvid Requate

The Bash builtin getopts function can be used to parse long options by putting a dash character followed by a colon into the optspec:

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: 
$ ./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'
[-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

After copying to executable file name=getopts_test.shin the current working directory, one can produce output like

复制到当前工作目录getopts_test.sh中的可执行文件 name=后,可以产生如下输出

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

Obviously getopts neither performs OPTERRchecking nor option-argument parsing for the long options. The script fragment above shows how this may be done manually. The basic principle also works in the Debian Almquist shell ("dash"). Note the special case:

显然 getopts 既不执行OPTERR检查也不执行长选项的选项参数解析。上面的脚本片段显示了如何手动完成此操作。基本原理也适用于 Debian Almquist shell(“破折号”)。注意特殊情况:

aflag=no
bflag=no
flist=""
set -- $(getopt abf: "$@")
while [ $# -gt 0 ]
do
    case "" in
    (-a) aflag=yes;;
    (-b) bflag=yes;;
    (-f) flist="$flist "; shift;;
    (--) shift; break;;
    (-*) echo "
aflag=no
bflag=no
cargument=none

# options may be followed by one colon to indicate they have a required argument
if ! options=$(getopt -o abc: -l along,blong,clong: -- "$@")
then
    # something went wrong, getopt will put out an error message for us
    exit 1
fi

set -- $options

while [ $# -gt 0 ]
do
    case  in
    -a|--along) aflag="yes" ;;
    -b|--blong) bflag="yes" ;;
    # for options with required arguments, an additional shift is required
    -c|--clong) cargument="" ; shift;;
    (--) shift; break;;
    (-*) echo "
die() { echo "$*" >&2; exit 2; }  # complain to STDERR and exit with error
needs_arg() { if [ -z "$OPTARG" ]; then die "No arg for --$OPT option"; fi; }

while getopts ab:c:-: OPT; do
  # support long options: https://stackoverflow.com/a/28466267/519360
  if [ "$OPT" = "-" ]; then   # long option: reformulate OPT and OPTARG
    OPT="${OPTARG%%=*}"       # extract long option name
    OPTARG="${OPTARG#$OPT}"   # extract long option argument (may be empty)
    OPTARG="${OPTARG#=}"      # if long option argument, remove assigning `=`
  fi
  case "$OPT" in
    a | alpha )    alpha=true ;;
    b | bravo )    needs_arg; bravo="$OPTARG" ;;
    c | charlie )  needs_arg; charlie="$OPTARG" ;;
    ??* )          die "Illegal option --$OPT" ;;  # bad long option
    \? )           exit 2 ;;  # bad short option (error reported via getopts)
  esac
done
shift $((OPTIND-1)) # remove parsed options and args from $@ list
: error - unrecognized option " 1>&2; exit 1;; (*) break;; esac shift done
: error - unrecognized option " 1>&2; exit 1;; (*) break;; esac shift done # Process remaining non-option arguments ...

Note that, as GreyCat from over at http://mywiki.wooledge.org/BashFAQpoints out, this trick exploits a non-standard behaviour of the shell which permits the option-argument (i.e. the filename in "-f filename") to be concatenated to the option (as in "-ffilename"). The POSIXstandard says there must be a space between them, which in the case of "-- longoption" would terminate the option-parsing and turn all longoptions into non-option arguments.

请注意,正如来自http://mywiki.wooledge.org/BashFAQ 的GreyCat指出的那样,此技巧利用了允许选项参数(即“-f 文件名”中的文件名)的 shell 的非标准行为连接到选项(如“-ffilename”)。该POSIX标准说必须有它们之间的空间,这在的情况下“ - longoption”将终止期权分析,把所有longoptions成非选项参数。

回答by Jonathan Leffler

The built-in getoptscommand is still, AFAIK, limited to single-character options only.

内置getopts命令仍然 AFAIK,仅限于单字符选项。

There is (or used to be) an external program getoptthat would reorganize a set of options such that it was easier to parse. You could adapt that design to handle long options too. Example usage:

有(或曾经有)一个外部程序getopt可以重新组织一组选项,以便更容易解析。您也可以调整该设计以处理长选项。用法示例:

no_arg() { if [ -n "$OPTARG" ]; then die "No arg allowed for --$OPT option"; fi; }

You could use a similar scheme with a getoptlongcommand.

您可以在getoptlong命令中使用类似的方案。

Note that the fundamental weakness with the external getoptprogram is the difficulty of handling arguments with spaces in them, and in preserving those spaces accurately. This is why the built-in getoptsis superior, albeit limited by the fact it only handles single-letter options.

请注意,外部getopt程序的根本弱点是难以处理包含空格的参数,并且难以准确地保留这些空格。这就是为什么内置getopts是优越的,尽管受到它只处理单字母选项的限制。

回答by sme

Here's an example that actually uses getopt with long options:

这是一个实际使用带有长选项的 getopt 的示例:

#!/bin/sh

# source shflags from current directory
. ./shflags

# define a 'name' command-line string flag
DEFINE_string 'name' 'world' 'name to say hello to' 'n'

# parse the command-line
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"

# say hello
echo "Hello, ${FLAGS_name}!"

回答by Adam Katz

Long options can be parsed by the standard getoptsbuiltin as “arguments” to the -“option”

标准getopts内置函数可以将长选项解析为-“选项”的“参数”

This is portable and native POSIX shell – no external programs or bashisms are needed.

这是可移植的本地 POSIX shell——不需要外部程序或 bashisms。

This guide implements long options as arguments to the -option, so --alphais seen by getoptsas -with argument alphaand --bravo=foois seen as -with argument bravo=foo. The true argument can be harvested with a simple replacement: ${OPTARG#*=}.

本指南工具长选项作为参数传递给-选项,因此--alpha被看作getopts-与参数alpha--bravo=foo被视为-与参数bravo=foo。真正的论点可以通过一个简单的替换来获得:${OPTARG#*=}.

In this example, -band -c(and their long forms, --bravoand --charlie) have mandatory arguments. Arguments to long options come after equals signs, e.g. --bravo=foo(space delimiters for long options would be hard to implement, see below).

在这个例子中,-band -c(和它们的长形式,--bravoand --charlie)有强制参数。长选项的参数在等号之后,例如--bravo=foo(长选项的空格分隔符将难以实现,见下文)。

Because this uses the getoptsbuiltin, this solution supports usage like cmd --bravo=foo -ac FILE(which has combined options -aand -cand interleaves long options with standard options) while most other answers here either struggle or fail to do that.

因为它使用了getoptsbuiltin,所以这个解决方案支持这样的用法cmd --bravo=foo -ac FILE(它结合了选项-a-c并且将长选项与标准选项交错),而这里的大多数其他答案要么挣扎要么失败。

$ ./hello_world.sh --name Kate
Hello, Kate!

When the option is a dash (-), it is a long option. getoptswill have parsed the actual long option into $OPTARG, e.g. --bravo=foooriginally sets OPT='-'and OPTARG='bravo=foo'. The ifstanza sets $OPTto the contents of $OPTARGbefore the first equals sign (bravoin our example) and then removes that from the beginning of $OPTARG(yielding =fooin this step, or an empty string if there is no =). Finally, we strip the argument's leading =. At this point, $OPTis either a short option (one character) or a long option (2+ characters).

When the option is a dash ( -), it is a long option. getopts会将实际的 long 选项解析为$OPTARG,例如--bravo=foo最初设置OPT='-'OPTARG='bravo=foo'。该if节设置$OPT$OPTARG第一个等号之前的内容(bravo在我们的示例中),然后从开头删除$OPTARG=foo在此步骤中产生,如果没有,则为空字符串=)。最后,我们去掉参数的前导=. 此时,$OPT要么是短选项(一个字符),要么是长选项(2+ 个字符)。

The casethen matches either short or long options. For short options, getoptsautomatically complains about options and missing arguments, so we have to replicate those manually using the needs_argfunction, which fatally exits when $OPTARGis empty. The ??*condition will match any remaining long option (?matches a single character and *matches zero or more, so ??*matches 2+ characters), allowing us to issue the "Illegal option" error before exiting.

case随后比赛无论是短期或长期的选项。对于短选项,会getopts自动抱怨选项和缺少参数,因此我们必须使用该needs_arg函数手动复制这些选项,该函数在$OPTARG为空时会致命地退出。该??*条件将匹配任何剩余的长选项(?匹配单个字符和*匹配零个或更多,所以??*匹配2+字符),使我们能够在退出之前发出“非法选项”的错误。

(A note about all-uppercase variable names: Generally, the advice is to reserve all-uppercase variables for system use. I'm keeping $OPTas all-uppercase to keep it in line with $OPTARG, but this does break that convention. I think it fits because this is something the system should have done, and it should be safe because there are no standards (afaik) that use such a variable.)

(关于全大写变量名的说明:通常,建议是保留全大写变量供系统使用。我保持$OPT全大写以使其与 保持一致$OPTARG,但这确实打破了该约定。我认为它适合因为这是系统应该做的事情,并且它应该是安全的,因为没有使用这样的变量的标准(afaik)。)



To complain about unexpected arguments to long options, mimic what we did for mandatory arguments: use a helper function. Just flip the test around to complain about an argument when one isn't expected:

要抱怨长选项的意外参数,请模仿我们对强制参数所做的:使用辅助函数。只需翻转测试以在不期望的情况下抱怨争论:

$ ./hello_world.sh -n Kate
Hello, Kate!


An older version of this answerhad an attempt at accepting long options with space-delimited arguments, but it was not reliable; getoptscould prematurely terminate on the assumption that the argument was beyond its scope and manually incrementing $OPTINDdoesn't work in all shells.

此答案旧版本尝试接受带有空格分隔参数的长选项,但它不可靠;getopts假设参数超出其范围并且手动递增$OPTIND不适用于所有 shell ,则可能会过早终止。

This would be accomplished using one of these techniques:

这将使用以下技术之一来完成:

and then concluded with something like [ $# -gt $OPTIND ] && OPTIND=$((OPTIND+1))

然后以类似的东西结束 [ $# -gt $OPTIND ] && OPTIND=$((OPTIND+1))

回答by Adam Katz

Take a look at shFlagswhich is a portable shell library (meaning: sh, bash, dash, ksh, zsh on Linux, Solaris, etc.).

看看shFlags,它是一个可移植的 shell 库(意思是:Linux、Solaris 上的 sh、bash、dash、ksh、zsh 等)。

It makes adding new flags as simple as adding one line to your script, and it provides an auto generated usage function.

它使添加新标志就像在脚本中添加一行一样简单,并且它提供了一个自动生成的使用功能。

Here is a simple Hello, world!using shFlag:

这是一个简单的Hello, world!使用shFlag

Options=$@
Optnum=$#
sfoo='no '
sbar='no '
sfoobar='no '
sbarfoo='no '
sarguments='no '
sARG=empty
lfoo='no '
lbar='no '
lfoobar='no '
lbarfoo='no '
larguments='no '
lARG=empty

For OSes that have the enhanced getopt that supports long options (e.g. Linux), you can do:

对于具有支持长选项的增强 getopt 的操作系统(例如 Linux),您可以执行以下操作:

function _usage() 
{
  ###### U S A G E : Help and ERROR ######
  cat <<EOF
   foobar $Options
  $*
          Usage: foobar <[options]>
          Options:
                  -b   --bar            Set bar to yes    ($foo)
                  -f   --foo            Set foo to yes    ($bart)
                  -h   --help           Show this message
                  -A   --arguments=...  Set arguments to yes ($arguments) AND get ARGUMENT ($ARG)
                  -B   --barfoo         Set barfoo to yes ($barfoo)
                  -F   --foobar         Set foobar to yes ($foobar)
EOF
}

[ $# = 0 ] && _usage "  >>>>>>>> no options given "

For the rest, you must use the short option:

对于其余部分,您必须使用短选项:

while getopts ':bfh-A:BF' OPTION ; do
  case "$OPTION" in
    b  ) sbar=yes                       ;;
    f  ) sfoo=yes                       ;;
    h  ) _usage                         ;;   
    A  ) sarguments=yes;sARG="$OPTARG"  ;;
    B  ) sbarfoo=yes                    ;;
    F  ) sfoobar=yes                    ;;
    -  ) [ $OPTIND -ge 1 ] && optind=$(expr $OPTIND - 1 ) || optind=$OPTIND
         eval OPTION="$$optind"
         OPTARG=$(echo $OPTION | cut -d'=' -f2)
         OPTION=$(echo $OPTION | cut -d'=' -f1)
         case $OPTION in
             --foo       ) lfoo=yes                       ;;
             --bar       ) lbar=yes                       ;;
             --foobar    ) lfoobar=yes                    ;;
             --barfoo    ) lbarfoo=yes                    ;;
             --help      ) _usage                         ;;
             --arguments ) larguments=yes;lARG="$OPTARG"  ;; 
             * )  _usage " Long: >>>>>>>> invalid options (long) " ;;
         esac
       OPTIND=1
       shift
      ;;
    ? )  _usage "Short: >>>>>>>> invalid options (short) "  ;;
  esac
done

Adding a new flag is as simple as adding a new DEFINE_ call.

添加一个新标志就像添加一个新的DEFINE_ call.

回答by RapaNui

Using getoptswith short/long options and arguments

使用getopts短/长期的选项和参数



Works with all combinations, e.g:

适用于所有组合,例如:

  • foobar -f --bar
  • foobar --foo -b
  • foobar -bf --bar --foobar
  • foobar -fbFBAshorty --bar -FB --arguments=longhorn
  • foobar -fA "text shorty" -B --arguments="text longhorn"
  • bash foobar -F --barfoo
  • sh foobar -B --foobar - ...
  • bash ./foobar -F --bar
  • foob​​ar -f --bar
  • foob​​ar --foo -b
  • foob​​ar -bf --bar --foobar
  • foob​​ar -fbFBAshorty --bar -FB --arguments=longhorn
  • foob​​ar -fA "text shorty" -B --arguments="text longhorn"
  • bash foobar -F --barfoo
  • sh foobar -B --foobar - ...
  • bash ./foobar -F --bar


Some declarations for this example

这个例子的一些声明

##################################################################
echo "----------------------------------------------------------"
echo "RESULT short-foo      : $sfoo                                    long-foo      : $lfoo"
echo "RESULT short-bar      : $sbar                                    long-bar      : $lbar"
echo "RESULT short-foobar   : $sfoobar                                 long-foobar   : $lfoobar"
echo "RESULT short-barfoo   : $sbarfoo                                 long-barfoo   : $lbarfoo"
echo "RESULT short-arguments: $sarguments  with Argument = \"$sARG\"   long-arguments: $larguments and $lARG"

How the Usage function would look

Usage 函数的外观

#!/bin/bash
# foobar: getopts with short and long options AND arguments

function _cleanup ()
{
  unset -f _usage _cleanup ; return 0
}

## Clear out nested functions on exit
trap _cleanup INT EXIT RETURN

###### some declarations for this example ######
Options=$@
Optnum=$#
sfoo='no '
sbar='no '
sfoobar='no '
sbarfoo='no '
sarguments='no '
sARG=empty
lfoo='no '
lbar='no '
lfoobar='no '
lbarfoo='no '
larguments='no '
lARG=empty

function _usage() 
{
  ###### U S A G E : Help and ERROR ######
  cat <<EOF
   foobar $Options
   $*
          Usage: foobar <[options]>
          Options:
                  -b   --bar            Set bar to yes    ($foo)
                    -f   --foo            Set foo to yes    ($bart)
                      -h   --help           Show this message
                  -A   --arguments=...  Set arguments to yes ($arguments) AND get ARGUMENT ($ARG)
                  -B   --barfoo         Set barfoo to yes ($barfoo)
                  -F   --foobar         Set foobar to yes ($foobar)
EOF
}

[ $# = 0 ] && _usage "  >>>>>>>> no options given "

##################################################################    
#######  "getopts" with: short options  AND  long options  #######
#######            AND  short/long arguments               #######
while getopts ':bfh-A:BF' OPTION ; do
  case "$OPTION" in
    b  ) sbar=yes                       ;;
    f  ) sfoo=yes                       ;;
    h  ) _usage                         ;;   
    A  ) sarguments=yes;sARG="$OPTARG"  ;;
    B  ) sbarfoo=yes                    ;;
    F  ) sfoobar=yes                    ;;
    -  ) [ $OPTIND -ge 1 ] && optind=$(expr $OPTIND - 1 ) || optind=$OPTIND
         eval OPTION="$$optind"
         OPTARG=$(echo $OPTION | cut -d'=' -f2)
         OPTION=$(echo $OPTION | cut -d'=' -f1)
         case $OPTION in
             --foo       ) lfoo=yes                       ;;
             --bar       ) lbar=yes                       ;;
             --foobar    ) lfoobar=yes                    ;;
             --barfoo    ) lbarfoo=yes                    ;;
             --help      ) _usage                         ;;
               --arguments ) larguments=yes;lARG="$OPTARG"  ;; 
             * )  _usage " Long: >>>>>>>> invalid options (long) " ;;
         esac
       OPTIND=1
       shift
      ;;
    ? )  _usage "Short: >>>>>>>> invalid options (short) "  ;;
  esac
done

getopswith long/short flags as well as long arguments

getops带有长/短标志以及长参数

# translate long options to short
for arg
do
    delim=""
    case "$arg" in
       --help) args="${args}-h ";;
       --verbose) args="${args}-v ";;
       --config) args="${args}-c ";;
       # pass through anything else
       *) [[ "${arg:0:1}" == "-" ]] || delim="\""
           args="${args}${delim}${arg}${delim} ";;
    esac
done
# reset the translated args
eval set -- $args
# now we can process with getopt
while getopts ":hvc:" opt; do
    case $opt in
        h)  usage ;;
        v)  VERBOSE=true ;;
        c)  source $OPTARG ;;
        \?) usage ;;
        :)
        echo "option -$OPTARG requires an argument"
        usage
        ;;
    esac
done

Output

输出

# A string with command options
options=$@

# An array with all the arguments
arguments=($options)

# Loop index
index=0

for argument in $options
  do
    # Incrementing index
    index=`expr $index + 1`

    # The conditions
    case $argument in
      -a) echo "key $argument value ${arguments[index]}" ;;
      -abc) echo "key $argument value ${arguments[index]}" ;;
    esac
  done

exit;


Combining the above into a cohesive script

将上述内容组合成一个有凝聚力的脚本

##代码##

回答by mtvee

Another way...

其它的办法...

##代码##

回答by mtvee

I kind of solved this way:

我是这样解决的:

##代码##

Am I being dumb or something? getoptand getoptsare so confusing.

是我笨还是怎么的?getopt并且getopts如此混乱。