使用 getopts 处理长命令行选项和短命令行选项
我希望使用我的 shell 脚本调用长形式和短形式的命令行选项。
我知道可以使用 getopts,但就像在 Perl 中一样,我无法使用 shell 执行相同的操作。
关于如何完成此操作的任何想法,以便我可以使用以下选项:
./shell.sh --copyfile abc.pl /tmp/
./shell.sh -c abc.pl /tmp/
在上面,这两个命令对我的 shell 意味着相同的事情,但使用 getopts
,我无法实现这些?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
getopt
和getopts
是不同的东西,人们似乎对它们的作用有一些误解。getopts
是bash
的内置命令,用于循环处理命令行选项,并将找到的每个选项和值依次分配给内置变量,这样您就可以进一步处理它们。 然而,getopt
是一个外部实用程序,它实际上并不为您处理选项,例如 bashgetopts
, PerlGetopt
模块或 Pythonoptparse
/argparse
模块可以做到这一点。 getopt 所做的只是规范化传入的选项,即将它们转换为更标准的形式,以便 shell 脚本更容易处理它们。 例如,getopt
的应用程序可能会将以下内容转换为
:您必须自己进行实际处理。 如果您对指定选项的方式做出各种限制,则根本不必使用 getopt:
-o
),该值必须作为单独的参数(在空格之后)。为什么使用
getopt
而不是getopts
? 基本原因是只有 GNUgetopt
为您提供对长命名命令行选项的支持。1(GNUgetopt
是 Linux 上的默认设置Mac OS X 和 FreeBSD 附带了一个基本且不太有用的getopt
,但可以安装 GNU 版本;请参见下文。)例如,这里是使用 GNU
getopt 的示例。
,来自我的一个名为javawrap
的脚本:这可以让您指定诸如
--verbose -dm4096 --minh=20 --maxhe 40 --debugfi="/Users 之类的选项/John Johnson/debug.txt"
或类似内容。 调用 getopt 的效果是将选项规范化为 --verbose -d -m 4096 --minheap 20 --maxheap 40 --debugfile "/Users/John Johnson/debug .txt" 以便您可以更轻松地处理它们。 围绕"$1"
和"$2"
的引用很重要,因为它确保正确处理其中包含空格的参数。如果删除前 9 行(从
eval set
行开始的所有内容),代码将仍然有效! 但是,您的代码在接受哪些类型的选项方面会更加挑剔:特别是,您必须以上述“规范”形式指定所有选项。 但是,通过使用 getopt,您可以对单字母选项进行分组,使用更短且无歧义的长选项形式,或者使用--file foo.txt
或--file=foo.txt
样式,使用-m 4096
或-m4096
样式,在任意文件中混合选项和非选项如果发现无法识别或不明确的选项,getopt
还会输出错误消息。注意:实际上有两个完全不同的 getopt 版本:基本的 getopt 和 GNU getopt code>,具有不同的功能和不同的调用约定。2 基本的
getopt
相当糟糕:它不仅不能处理长选项,甚至不能处理嵌入的空格在参数或空参数内部,而 getopts 确实可以做到这一点。 上面的代码在基本的getopt
中不起作用。 GNUgetopt
在 Linux 上默认安装,但在 Mac OS X 和 FreeBSD 上需要单独安装。 在 Mac OS X 上,安装 MacPorts (http://www.macports.org),然后执行sudo port install getopt
安装 GNUgetopt
(通常安装到/opt/local/bin
中),并确保/opt/local/bin< /code> 在 shell 路径中位于
/usr/bin
之前。 在 FreeBSD 上,安装misc/getopt
。修改您自己的程序的示例代码的快速指南:在前几行中,除了调用
getopt
的行之外,所有内容都是“样板文件”,应该保持不变。 您应该更改-n
后的程序名称,在-o
后指定短选项,在--long
后指定长选项。 在带有值的选项后面放置一个冒号。最后,如果您看到代码只有
set
而不是eval set
,那么它是为 BSDgetopt
编写的。 您应该将其更改为使用eval set
样式,该样式在getopt
的两个版本中都可以正常工作,而普通的set
则无法正常工作使用 GNU getopt。1实际上,
ksh93
中的getopts
支持长命名选项,但此 shell 的使用频率不如bash
。 在zsh
中,使用zparseopts
来获取此功能。2从技术上讲,“GNU
getopt
”是一个用词不当; 这个版本实际上是为 Linux 而不是 GNU 项目编写的。 然而,它遵循所有 GNU 约定,并且通常使用术语“GNUgetopt
”(例如在 FreeBSD 上)。getopt
andgetopts
are different beasts, and people seem to have a bit of misunderstanding of what they do.getopts
is a built-in command tobash
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 you the way that e.g. bashgetopts
, the PerlGetopt
module or the Pythonoptparse
/argparse
modules do. All thatgetopt
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 ofgetopt
might convert the following:into this:
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:-o
above), the value has to go as a separate argument (after a space).Why use
getopt
instead ofgetopts
? The basic reason is that only GNUgetopt
gives you support for long-named command-line options.1 (GNUgetopt
is the default on Linux. Mac OS X and FreeBSD come with a basic and not-very-usefulgetopt
, but the GNU version can be installed; see below.)For example, here's an example of using GNU
getopt
, from a script of mine calledjavawrap
: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 togetopt
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.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 ofgetopt
, 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.NOTE: There are actually two totally different versions of
getopt
, basicgetopt
and GNUgetopt
, with different features and different calling conventions.2 Basicgetopt
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, whereasgetopts
does do this right. The above code will not work in basicgetopt
. GNUgetopt
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 dosudo port install getopt
to install GNUgetopt
(usually into/opt/local/bin
), and make sure that/opt/local/bin
is in your shell path ahead of/usr/bin
. On FreeBSD, installmisc/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.Finally, if you see code that has just
set
instead ofeval set
, it was written for BSDgetopt
. You should change it to use theeval set
style, which works fine with both versions ofgetopt
, while the plainset
doesn't work right with GNUgetopt
.1Actually,
getopts
inksh93
supports long-named options, but this shell isn't used as often asbash
. Inzsh
, usezparseopts
to get this functionality.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 "GNUgetopt
" is commonly used (e.g. on FreeBSD).可以考虑三种实现:
Bash 内置
getopts
。 这不支持带有双破折号前缀的长选项名称。 它仅支持单字符选项。独立
getopt
命令的 BSD UNIX 实现(MacOS 使用的)。 这也不支持长选项。独立
getopt
的GNU实现。 GNUgetopt(3)
(由命令行使用getopt(1)
在 Linux 上)支持解析长选项。其他一些答案显示了使用 bash 内置 getopts 来模拟长选项的解决方案。 该解决方案实际上制作了一个字符为“-”的短选项。 所以你得到“--”作为标志。 然后后面的任何内容都会成为 OPTARG,并且您可以使用嵌套的
case
来测试 OPTARG。这很聪明,但也有一些警告:
getopts
无法强制执行 opt 规范。 如果用户提供无效选项,它不会返回错误。 在解析 OPTARG 时,您必须自己进行错误检查。因此,虽然可以编写更多代码来解决缺乏对长选项的支持的问题,但这需要更多的工作,并且部分地违背了使用 getopt 解析器来简化代码的目的。
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.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 nestedcase
.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.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.
Bash 内置 getopts 函数可用于解析长选项,方法是将破折号字符后跟冒号放入 optspec
中:复制到 当前工作目录,可以产生类似的输出
显然 getopts 既不执行
OPTERR
检查也不执行选项参数解析对于长选项。 上面的脚本片段显示了如何手动完成此操作。 基本原理也适用于 Debian Almquist shell(“dash”)。 请注意特殊情况:请注意,正如来自 http://mywiki.wooledge.org/BashFAQ 指出,这个技巧利用了 shell 的非标准行为,它允许选项参数(即“-f filename”中的文件名)连接到选项(如“-ffilename”中)。 POSIX 标准规定它们之间必须有一个空格,在“--longoption”的情况将终止选项解析并将所有长选项转换为非选项参数。
The Bash builtin getopts function can be used to parse long options by putting a dash character followed by a colon into the optspec:
After copying to executable file name=
getopts_test.sh
in the current working directory, one can produce output likeObviously 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:Note that, as GreyCat from over at http://mywiki.wooledge.org/BashFAQ points 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 POSIX standard 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.
长选项可以由内置的标准
getopts
解析为-
“选项”的“参数”这是可移植的本机 POSIX shell – 没有外部程序或 bashism
本指南将长选项实现为
-
选项的参数,因此--alpha
被getopts
视为 <带有参数alpha
和--bravo=foo
的 code>- 被视为带有参数bravo=foo< 的
-
/代码>。 真正的参数是通过 shell 参数扩展、更新$OPT
和$OPTARG
获得的。在此示例中,
-b
、--bravo
和--charlie
接受参数。-b
/--bravo
需要参数,--charlie
接受可选参数,-c
禁止参数(getopts
不支持可选参数,因此指定选项c
时没有使用:
,因为这会需要一个论点)。 长选项的参数遵循等号,例如--bravo=foo
(长选项的空格分隔符不能可靠地移植,请参见下文)。通过使用
getopts
内置,这支持像cmd --charlie=chaplin -ab FILE
这样的用法,它具有组合选项-a
和-b
并将长选项与标准选项交错,这是这里许多其他答案要么努力要么失败的事情。当选项是短划线 (
-
) 时,它是一个长选项。getopts
会将实际的长选项解析为$OPTARG
,例如--bravo=foo
最初设置OPT='-'< /code> 和
OPTARG='bravo=foo'
。if
节将$OPT
设置为第一个等号之前的$OPTARG
的内容(在我们的示例中为bravo
),然后从$OPTARG
的开头删除它(在此步骤中生成=foo
,如果没有=
则生成空字符串) 。 最后,我们去掉参数的前导=
。 此时,$OPT
要么是短选项(一个字符),要么是长选项(2 个以上字符)。您可以接受长选项的可选参数!
--charlie
省略needs_arg
并分配给${OPTARG:-$charlie_default}< /code>,当
$OPTARG
为空时使用$charlie_default
。 (getopts
不允许使用-c
的可选参数,因此禁止它。)然后
case
匹配短选项或长选项(管道,|
,表示“或”运算,例如delta ) delta=true ;;
不需要管道。 对于短选项,getopts
会自动抱怨选项和缺少参数,因此我们必须使用needs_arg
函数手动复制这些选项,以便在$OPTARG
时致命退出代码> 为空。当 getopts 看到未指定的短选项时,它会抛出自己的错误并将 $OPT 转换为问号。
\?
条件匹配该条件并静默退出。 最后的*
匹配其他任何内容,这应该是无效的长选项,模仿getopts
错误消息,然后退出。与 GNU 风格的长选项一样,提供
--
将停止解析,因此-a -- --bravo=4
将设置$alpha 更改为
true
,但$bravo
将保持不变,而$1
将为--bravo=4
。 我不建议使用前导破折号来命名文件,但这就是您表示它们不是选项的方式。大写变量名称:通常,建议保留全大写变量供系统使用。 我将
$OPT
保留为全大写,以使其与$OPTARG
保持一致,但这确实打破了该约定。 我认为它很合适,因为这是系统应该做的事情,而且应该是安全的; 我还没有听说过任何使用此变量名称的标准。抱怨长选项的意外参数:通过翻转测试模仿
needs_arg
来抱怨意外的参数:接受带有空格的长选项-分隔参数:您可以使用
eval "ARG_B=\"\$$OPTIND\""
(或使用 bash 的 间接扩展,ARG_B="${!OPTIND}"
),然后按说明递增$OPTIND
在此答案的旧版本中,但它不可靠; 如果参数超出其范围,并且某些实现不适合手动操作$OPTIND
,则getopts
可能会提前终止。Long options can be parsed by the standard
getopts
builtin as “arguments” to the-
“option”This is portable and native POSIX shell – no external programs or bashisms are needed.
This guide implements long options as arguments to the
-
option, so--alpha
is seen bygetopts
as-
with argumentalpha
and--bravo=foo
is seen as-
with argumentbravo=foo
. The true argument is harvested via shell parameter expansion, updating$OPT
and$OPTARG
.In this example, arguments are accepted for
-b
,--bravo
, and--charlie
.-b
/--bravo
requires an argument,--charlie
accepts an optional argument, and-c
prohibits arguments (getopts
doesn't support optional arguments, so optionc
was specified without a:
since that would require an argument). Arguments to long options follow equals signs, like--bravo=foo
(space delimiters for long options aren't reliably portable, see below).By using the
getopts
builtin, this supports usage likecmd --charlie=chaplin -ab FILE
, which has combined options-a
and-b
and interleaves long options with standard options, something many other answers here either struggle or fail to do.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 setsOPT='-'
andOPTARG='bravo=foo'
. Theif
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).You can accept optional arguments to long options!
--charlie
omitsneeds_arg
and assigns to${OPTARG:-$charlie_default}
, which uses$charlie_default
when$OPTARG
is empty. (getopts
can't allow an optional argument for-c
and therefore prohibits it.)The
case
then matches either short or long options (the pipe,|
, indicates that "or" operation. A long-only option likedelta ) delta=true ;;
doesn't need a pipe). For short options,getopts
automatically complains about options and missing arguments, so we have to manually replicate those by using theneeds_arg
function to fatally exit when$OPTARG
is empty.When
getopts
sees an unspecified short option, it throws its own error and converts$OPT
to a question mark. The\?
condition matches that and exits silently. The final*
matches anything else, which should be an invalid long option, mimics thegetopts
error message, and then exits.As is normal for GNU-style long options, providing
--
will stop parsing, so-a -- --bravo=4
will set$alpha
totrue
but$bravo
will remain untouched and$1
will be--bravo=4
. I don't recommend naming files with leading dashes, but this is how you denote they aren't options.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; I haven't heard of any standards that use this variable name.To complain about unexpected arguments to long options: Mimic
needs_arg
with a flipped test to complain about an argument when one isn't expected:To accept long options with space-delimited arguments: You can pull in the next argument with
eval "ARG_B=\"\$$OPTIND\""
(or with bash's indirect expansion,ARG_B="${!OPTIND}"
) and then increment$OPTIND
as noted in an older version of this answer, but it's unreliable;getopts
can prematurely terminate on the assumption that the argument was beyond its scope and some implementations don't take well to manual manipulations of$OPTIND
.AFAIK 内置的 getopts 命令仍然仅限于单字符选项。
有(或曾经有)一个外部程序
getopt
可以重新组织一组选项,以便更容易解析。 您也可以调整该设计来处理长选项。 用法示例:您可以通过
getoptlong
命令使用类似的方案。请注意,外部 getopt 程序的根本弱点是难以处理带空格的参数以及准确保留这些空格。 这就是为什么内置的 getopts 更优越,尽管它只处理单字母选项。
The built-in
getopts
command is still, AFAIK, limited to single-character options only.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:You could use a similar scheme with a
getoptlong
command.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-ingetopts
is superior, albeit limited by the fact it only handles single-letter options.这是一个实际使用 getopt 和长选项的示例:
Here's an example that actually uses getopt with long options:
将
getopts
与短/长选项和参数一起使用适用于所有组合,例如:
此示例的一些声明
使用函数看起来如何
使用长/短标志以及长参数的
getops
输出将以上内容组合成一个有凝聚力的脚本
Using
getopts
with short/long options and argumentsWorks with all combinations, e.g:
Some declarations for this example
How the Usage function would look
getops
with long/short flags as well as long argumentsOutput
Combining the above into a cohesive script
看一下shFlags,它是一个可移植的shell库(意思是:sh, Linux、Solaris 等上的 bash、dash、ksh、zsh)。
它使添加新标志就像在脚本中添加一行一样简单,并且它提供了自动生成的使用功能。
这是一个使用 shFlag 的简单
Hello, world!
:对于具有支持长选项的增强 getopt 的操作系统(例如 Linux),您可以执行以下操作:
对于其余的,您 可以必须使用短选项:
添加新标志就像添加新的
DEFINE_调用
一样简单。Take a look at shFlags which is a portable shell library (meaning: sh, bash, dash, ksh, zsh on Linux, Solaris, etc.).
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:For OSes that have the enhanced getopt that supports long options (e.g. Linux), you can do:
For the rest, you must use the short option:
Adding a new flag is as simple as adding a new
DEFINE_ call
.其他方式...
Another way...
我是这样解决的:
我是愚蠢还是什么?
getopt
和getopts
太令人困惑了。I kind of solved this way:
Am I being dumb or something?
getopt
andgetopts
are so confusing.如果您不想要
getopt
依赖项,您可以这样做:当然,那么您就不能使用带有破折号的长样式选项。 如果您想添加缩短的版本(例如 --verbos 而不是 --verbose),那么您需要手动添加它们。
但如果您希望获得
getopts
功能以及长选项,这是一种简单的方法。我还将这段代码放在要点中。
In case you don't want the
getopt
dependency, you can do this:Of course, then you can't use long style options with one dash. And if you want to add shortened versions (e.g. --verbos instead of --verbose), then you need to add those manually.
But if you are looking to get
getopts
functionality along with long options, this is a simple way to do it.I also put this snippet in a gist.
内置的
getopts
无法做到这一点。 有一个外部 getopt(1) 程序可以执行此操作,但您只能在 Linux 上从 util-linux 软件包中获取它。 它带有一个示例脚本getopt-parse.bash。还有一个作为 shell 函数编写的
getopts_long
。The built-in
getopts
can't do this. There is an external getopt(1) program that can do this, but you only get it on Linux from the util-linux package. It comes with an example script getopt-parse.bash.There is also a
getopts_long
written as a shell function.。
.
接受的答案很好地指出了 bash 内置 getopts 的所有缺点。 答案的结尾是:
尽管我原则上同意这一说法,但我觉得我们在各种脚本中实现此功能的次数证明了我们应该付出一些努力来创建一个“标准化”的、经过良好测试的解决方案。
因此,我通过实现 getopts 中的 bash ="noreferrer">
getopts_long
在纯 bash 中,没有外部依赖项。 该函数的使用与内置getopts
100%兼容。通过在脚本中包含
getopts_long
(托管在 GitHub 上),答案对于原来的问题可以简单地实现为:The accepted answer does a very nice job of pointing out all the shortcomings of bash built-in
getopts
. The answer ends with:And even though I agree in principle with that statement, I feel that the number of times we all implemented this feature in various scripts justifies putting a bit of effort into creating a "standardised", well tested solution.
As such, I've "upgraded" bash built in
getopts
by implementinggetopts_long
in pure bash, with no external dependencies. The usage of the function is 100% compatible with the built-ingetopts
.By including
getopts_long
(which is hosted on GitHub) in a script, the answer to the original question can be implemented as simply as:在
ksh93
中,getopts
确实支持长名称......或者我找到的教程是这么说的。 尝试一下看看。
In
ksh93
,getopts
does support long names...Or so the tutorials I have found have said. Try it and see.
发明轮子的另一个版本...
这个函数(希望)是 GNU getopt 的 POSIX 兼容的普通 bourne shell 替代品。 它支持短/长选项,可以接受强制/可选/无参数,并且指定选项的方式几乎与 GNU getopt 相同,因此转换很简单。
当然,这仍然是要放入脚本中的相当大的代码块,但它大约是众所周知的 getopt_long shell 函数的一半行,并且在您只想替换现有 GNU getopt 使用的情况下可能更可取。
这是相当新的代码,所以 YMMV(如果由于任何原因这实际上不兼容 POSIX,请务必让我知道——可移植性是从一开始的意图,但我没有有用的 POSIX 测试环境)。
代码和示例用法如下:
示例用法:
Inventing yet another version of the wheel...
This function is a (hopefully) POSIX-compatible plain bourne shell replacement for GNU getopt. It supports short/long options which can accept mandatory/optional/no arguments, and the way in which options are specified is almost identical to GNU getopt, so conversion is trivial.
Of course this is still a sizeable chunk of code to drop into a script, but it's about half the lines of the well-known getopt_long shell function, and might be preferable in cases where you just want to replace existing GNU getopt uses.
This is pretty new code, so YMMV (and definitely please let me know if this isn't actually POSIX-compatible for any reason -- portability was the intention from the outset, but I don't have a useful POSIX test environment).
Code and example usage follows:
Example usage:
我只是偶尔写一下 shell 脚本,但很少练习,所以任何反馈都是值得赞赏的。
使用@Arvid Requate提出的策略,我们注意到一些用户错误。 忘记包含值的用户会意外地将下一个选项的名称视为值:
将导致“loglevel”的值被视为“--toc=TRUE”。 这个可以
应避免。
我改编了一些有关检查 CLI 用户错误的想法 http://mwiki.wooledge.org/BashFAQ/035< /a> 手动解析的讨论。 我在处理“-”和“--”参数时插入了错误检查。
然后我开始摆弄语法,所以这里的任何错误完全是我的错,而不是原作者的错。
我的方法可以帮助喜欢输入长字符(带或不带等号)的用户。 也就是说,它对“--loglevel 9”的响应应该与“--loglevel=9”相同。 在 --/space 方法中,无法确定用户是否忘记了参数,因此需要进行一些猜测。
如果您开始使用此参数,则“ --opt=value”和“--opt value”格式。 使用等号,命令行参数被视为“opt=value”,处理该问题的工作是字符串解析,以“=”分隔。 相反,对于“--opt value”,参数的名称是“opt”,我们面临着获取命令行中提供的下一个值的挑战。 这就是 @Arvid Requate 使用 ${!OPTIND} 的地方,即间接引用。 我仍然不明白这一点,嗯,根本不明白,BashFAQ 中的评论似乎警告不要这种风格(http ://mywiki.wooledge.org/BashFAQ/006)。 顺便说一句,我认为之前的发帖者关于 OPTIND=$(( $OPTIND + 1 )) 重要性的评论是不正确的。 我的意思是说,我认为忽略它并没有什么坏处。
在此脚本的最新版本中,标志 -v 表示详细打印输出。
将其保存在名为“cli-5.sh”的文件中,使可执行文件中的任何一个都可以工作,或者以所需的方式失败
这是对用户 intpu 进行错误检查的示例输出
您应该考虑打开 -v,因为它打印出 OPTIND 和 OPTARG 的内部结构
I only write shell scripts now and then and fall out of practice, so any feedback is appreciated.
Using the strategy proposed by @Arvid Requate, we noticed some user errors. A user who forgets to include a value will accidentally have the next option's name treated as a value:
will cause the value of "loglevel" to be seen as "--toc=TRUE". This can
be avoided.
I adapted some ideas about checking user error for CLI from http://mwiki.wooledge.org/BashFAQ/035 discussion of manual parsing. I inserted error checking into handling both "-" and "--" arguments.
Then I started fiddling around with the syntax, so any errors in here are strictly my fault, not the original authors.
My approach helps users who prefer to enter long with or without the equal sign. That is, it should have same response to "--loglevel 9" as "--loglevel=9". In the --/space method, it is not possible to know for sure if the user forgets an argument, so some guessing is needed.
In case you are starting out on this, there is an interesting difference between "--opt=value" and "--opt value" formats. With the equal sign, the command line argument is seen as "opt=value" and the work to handle that is string parsing, to separate at the "=". In contrast, with "--opt value", the name of the argument is "opt" and we have the challenge of getting the next value supplied in the command line. That's where @Arvid Requate used ${!OPTIND}, the indirect reference. I still don't understand that, well, at all, and comments in BashFAQ seem to warn against that style (http://mywiki.wooledge.org/BashFAQ/006). BTW, I don't think previous poster's comments about importance of OPTIND=$(( $OPTIND + 1 )) are correct. I mean to say, I see no harm from omitting it.
In newest version of this script, flag -v means VERBOSE printout.
Save it in a file called "cli-5.sh", make executable, and any of these will work, or fail in the desired way
Here is example output of the error-checking on user intpu
You should consider turning on -v, because it prints out internals of OPTIND and OPTARG
我还没有足够的代表来评论或投票支持他的解决方案,但是sme的答案对我来说非常有效。 我遇到的唯一问题是参数最终用单引号括起来(所以我将它们去掉)。
我还添加了一些示例用法和帮助文本。 我将在这里包含我的稍微扩展的版本:
I don't have enough rep yet to comment or vote his solution up, but sme's answer worked extremely well for me. The only issue I ran into was that the arguments end up wrapped in single-quotes (so I have an strip them out).
I also added some example usages and HELP text. I'll included my slightly extended version here:
在这里您可以找到 bash 中复杂选项解析的几种不同方法:
http://mywiki.wooledge.org/ComplexOptionParsing
我确实创建了以下一个,我认为这是一个很好,因为它的代码最少
多头和空头选项都有效。 使用这种方法,长选项也可以有多个参数。
Here you can find a few different approaches for complex option parsing in bash:
http://mywiki.wooledge.org/ComplexOptionParsing
I did create the following one, and I think it's a good one, because it's minimal code
and both long and short options work. A long option can also have multiple arguments with this approach.
如果这就是您想要调用脚本的方式
,那么您可以按照最简单的方法在 getopt 和 --longoptions 的帮助下实现它,
尝试这个,希望这有用
if simply this is how you want to call the script
then you can follow this simplest way to achieve it with the help of getopt and --longoptions
try this , hope this is useful
我已经在这个主题上工作了相当长的时间......并制作了我自己的库,您需要在主脚本中获取它。
请参阅 libopt4shell 和 cd2mpc 为例。
希望能帮助到你 !
I have been working on that subject for quite a long time... and made my own library which you will need to source in your main script.
See libopt4shell and cd2mpc for an example.
Hope it helps !
改进的解决方案:
An improved solution:
如果需要长命令行选项,也许使用 ksh 更简单,仅用于 getopts 部分,因为在那里可以更轻松地完成。
Maybe it's simpler to use ksh, just for the getopts part, if need long command line options, as it can be easier done there.
我想要一些没有外部依赖、有严格的 bash 支持 (-u) 的东西,而且我需要它甚至可以在旧的 bash 版本上工作。 这可以处理各种类型的参数:
只需在脚本顶部插入以下内容:
并像这样使用它:
I wanted something without external dependencies, with strict bash support (-u), and I needed it to work on even the older bash versions. This handles various types of params:
Just insert the following at the top of your script:
And use it like so:
这花了一些时间,但我想要这一切:
script.sh /file - V
或script.sh -V /file
)最后我想出了以下解决方案,它使用 getopt 捕获错误并将非选项移动到列表末尾,然后使用 getopts 解析短选项和长选项。
所有选项都会自动解析,并将其长选项名称作为变量名称(查看示例):
现在,我只需要定义所需的选项名称和
source
脚本:在调用脚本时,它是可以以完全随机的顺序传递所有选项和非选项:
注释
options
数组中在选项名称后添加:
以启用参数。$opt_X
,其中X
是短选项名称。[0]=long_only
完成的示例所示。 当然,每个数组索引必须是唯一的。使用的技术
stderr
:
使用getopt
解析参数getopts< 解析长选项名称/代码>
It took some time, but I wanted it all:
script.sh /file -V
orscript.sh -V /file
)Finally I came up with the following solution, which uses
getopt
to catch errors and move non-options to the end of the list and after thatgetopts
, which parses the short and long options.All options are automatically parsed with their long option name as variable name (look at the example):
Now, I only need to define the needed option names and
source
the script:And while calling the script, it's possible to pass all options and non-options in a complete random order:
Notes
options
array add:
after an option name to enable arguments.$opt_X
, whereX
is the short option name.[0]=long_only
. Of course every array index must be unique.Used techniques
stderr
without using a temporary file:
to parse arguments withgetopt
getopts
getopts“可以用于”解析长选项,只要您不希望它们有参数...
以下是如何执行的:
如果您尝试使用 OPTIND 来获取长选项的参数,getopts 会将其视为首先没有可选的位置参数,并且将停止解析任何其他参数。
在这种情况下,您最好使用简单的 case 语句手动处理它。
这将“总是”有效:
尽管它不像 getopts 那么灵活,并且您必须在案例实例中自己执行大部分错误检查代码......
但它是一个选项。
getopts "could be used" for parsing long options as long as you don't expect them to have arguments...
Here's how to:
If you try to use OPTIND for getting a parameter for the long option, getopts will treat it as the first no optional positional parameter and will stop parsing any other parameters.
In such a case you'll be better off handling it manually with a simple case statement.
This will "always" work:
Albeit is not as flexible as getopts and you have to do much of the error checking code yourself within the case instances...
But it is an option.
为了保持跨平台兼容,并避免对外部可执行文件的依赖,我从另一种语言移植了一些代码。
我发现它非常容易使用,这里有一个例子:
所需的 BASH 比它可能的要长一点,但我想避免依赖 BASH 4 的关联数组。 您也可以直接从 http://nt4.com/bash/argparser.inc.sh< /a>
In order to stay cross-platform compatible, and avoid the reliance on external executables, I ported some code from another language.
I find it very easy to use, here is an example:
The required BASH is a little longer than it could be, but I wanted to avoid reliance on BASH 4's associative arrays. You can also download this directly from http://nt4.com/bash/argparser.inc.sh
如果您所有的长选项都有唯一且匹配的第一个字符作为短选项,那么例如
Is the same as
您可以使用此 before getopts 来重写 $args:
感谢 mtvee 的启发; -)
If all your long options have unique, and matching, first characters as the short options, so for example
Is the same as
You can use this before getopts to rewrite $args:
Thanks for mtvee for the inspiration ;-)
内置
getopts
仅解析短选项(ksh93 中除外),但您仍然可以添加几行脚本以使 getopts 处理长选项。
这是在 http:// /www.uxora.com/unix/shell-script/22-handle-long-options-with-getopts
这是一个测试:
否则在最近的 Korn Shell ksh93,
getopts 可以自然地解析长选项,甚至可以显示类似的手册页。 (参见 http://www .uxora.com/unix/shell-script/20-getopts-with-man-page-and-long-options)
Builtin
getopts
only parse short options (except in ksh93),but you can still add few lines of scripting to make getopts handles long options.
Here is a part of code found in http://www.uxora.com/unix/shell-script/22-handle-long-options-with-getopts
Here is a test:
Otherwise in recent Korn Shell ksh93,
getopts
can naturally parse long options and even display a man page alike. (See http://www.uxora.com/unix/shell-script/20-getopts-with-man-page-and-long-options)内置的 OS X (BSD) getopt 不支持长选项,但 GNU 版本支持:
brew install gnu-getopt
。 然后,类似于:cp /usr/local/Cellar/gnu-getopt/1.1.6/bin/getopt /usr/local/bin/gnu-getopt。Th built-in OS X (BSD) getopt does not support long options, but the GNU version does:
brew install gnu-getopt
. Then, something similar to:cp /usr/local/Cellar/gnu-getopt/1.1.6/bin/getopt /usr/local/bin/gnu-getopt
.