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
Using getopts to process long and short command line options
提问by gagneet
I wish to have long and short forms of command line options invoked using my shell script.
我希望使用我的 shell 脚本调用长形式和短形式的命令行选项。
I know that getopts
can 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
getopt
command (which is what MacOS uses). This does not support long options either.GNU implementation of standalone
getopt
. GNUgetopt(3)
(used by the command-linegetopt(1)
on Linux) supports parsing long options.
Bash 内置
getopts
. 这不支持带有双破折号前缀的长选项名称。它仅支持单字符选项。独立
getopt
命令的BSD UNIX 实现(这是 MacOS 使用的)。这也不支持长选项。
Some other answers show a solution for using the bash builtin getopts
to 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:
这很聪明,但它带有警告:
getopts
can'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
getopt
and getopts
are different beasts, and people seem to have a bit of misunderstanding of what they do. getopts
is a built-in command to bash
to 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 Getopt
module or the Python optparse
/argparse
modules do. All that getopt
does 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 getopt
might 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 getopt
at 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.
-o
above), the value has to go as a separate argument (after a space).
- 每个参数只放一个选项;
- 所有选项都在任何位置参数之前(即非选项参数);
- 对于带有值的选项(例如
-o
上面),该值必须作为一个单独的参数(在一个空格之后)。
Why use getopt
instead of getopts
? The basic reason is that only GNU getopt
gives you support for long-named command-line options.1(GNU getopt
is 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 getopt
is 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 set
line), 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.txt
or --file=foo.txt
style, use either the -m 4096
or -m4096
style, mix options and non-options in any order, etc. getopt
also outputs an error message if unrecognized or ambiguous options are found.
如果您删除前 9 行(该eval set
行的所有内容),代码仍然可以工作!但是,您的代码在接受哪些选项方面会更加挑剔:特别是,您必须以上述“规范”形式指定所有选项。getopt
但是,通过使用,您可以对单字母选项进行分组,使用较短的非歧义形式的长选项,使用--file foo.txt
or--file=foo.txt
样式,使用-m 4096
or-m4096
样式,以任何顺序混合选项和非选项等。 getopt
如果发现无法识别或不明确的选项,也会输出错误消息。
NOTE: There are actually two totally differentversions of getopt
, basic getopt
and GNU getopt
, with different features and different calling conventions.2Basic getopt
is 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 getopts
does do this right. The above code will not work in basic getopt
. GNU getopt
is 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 getopt
to install GNU getopt
(usually into /opt/local/bin
), and make sure that /opt/local/bin
is 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 set
instead of eval set
, it was written for BSD getopt
. You should change it to use the eval set
style, which works fine with both versions of getopt
, while the plain set
doesn't work right with GNU getopt
.
最后,如果您看到的代码只是set
代替eval set
,则它是为 BSD 编写的getopt
。您应该将其更改为使用eval set
样式,该样式适用于 的两个版本getopt
,而普通样式set
不适用于 GNU getopt
。
1Actually, getopts
in ksh93
supports long-named options, but this shell isn't used as often as bash
. In zsh
, use zparseopts
to get this functionality.
1实际上,getopts
inksh93
支持长命名选项,但是这个 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.sh
in 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 OPTERR
checking 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 getopts
command is still, AFAIK, limited to single-character options only.
内置getopts
命令仍然 AFAIK,仅限于单字符选项。
There is (or used to be) an external program getopt
that 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 getoptlong
command.
您可以在getoptlong
命令中使用类似的方案。
Note that the fundamental weakness with the external getopt
program is the difficulty of handling arguments with spaces in them, and in preserving those spaces accurately. This is why the built-in getopts
is 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 getopts
builtin 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 --alpha
is seen by getopts
as -
with argument alpha
and --bravo=foo
is 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, -b
and -c
(and their long forms, --bravo
and --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).
在这个例子中,-b
and -c
(和它们的长形式,--bravo
and --charlie
)有强制参数。长选项的参数在等号之后,例如--bravo=foo
(长选项的空格分隔符将难以实现,见下文)。
Because this uses the getopts
builtin, this solution supports usage like cmd --bravo=foo -ac FILE
(which has combined options -a
and -c
and interleaves long options with standard options) while most other answers here either struggle or fail to do that.
因为它使用了getopts
builtin,所以这个解决方案支持这样的用法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. getopts
will have parsed the actual long option into $OPTARG
, e.g. --bravo=foo
originally sets OPT='-'
and OPTARG='bravo=foo'
. The if
stanza sets $OPT
to the contents of $OPTARG
before the first equals sign (bravo
in our example) and then removes that from the beginning of $OPTARG
(yielding =foo
in this step, or an empty string if there is no =
). Finally, we strip the argument's leading =
. At this point, $OPT
is 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 case
then matches either short or long options. For short options, getopts
automatically complains about options and missing arguments, so we have to replicate those manually using the needs_arg
function, which fatally exits when $OPTARG
is 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 $OPT
as 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; getopts
could prematurely terminate on the assumption that the argument was beyond its scope and manually incrementing $OPTIND
doesn't work in all shells.
此答案的旧版本尝试接受带有空格分隔参数的长选项,但它不可靠;getopts
假设参数超出其范围并且手动递增$OPTIND
不适用于所有 shell ,则可能会过早终止。
This would be accomplished using one of these techniques:
这将使用以下技术之一来完成:
- POSIX eval:
eval "bravo=\"\$$OPTIND\""
- Bash indirect expansion:
bravo="${!OPTIND}"
- Zsh parameter expansion
P
flag:bravo="${(P)OPTIND}"
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 getopts
with 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
- 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
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
getops
with 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? getopt
and getopts
are so confusing.
是我笨还是怎么的?getopt
并且getopts
如此混乱。