getopts简易教程
当你想用一种专业的方式解析命令行参数时,getopts就是要选择的工具。和它的旧版本兄弟命令getopt不同(注意没有s!),getopts是shell内置命令。高级地方表现在
- 你不需要通过一个外部命令传递参数
- getopts可以很容易的设置一些你能用于解析参数的变量(对于一个外部程序来说这是不可能的!)
- 你不必再处理过去一些使用getopt时的一些bug实现(空格, …)
- getopts已经在POSIX®定义
一些解析位置参数的其他方法(不用getopt(s))在这里介绍了: .
注意getopts不能解析GNU风格的长选项(--myoption)或XF86风格的长选项(-myoption)!
介绍
术语
需要先了解一下我们这里探讨的事情,所以让我们来看一个范例… 来看一下下面这行命令:
mybackup -x -f /etc/mybackup.conf -r ./foo.txt ./bar.txt
所有这些都叫位置参数,但是你可以把他们分成一些逻辑组:
- -x是一个选项, 一个标识, 一个开关: 一个字符, 有一个前引导短横杠(-)
- -f也是一个选项,但是这个选项有一个附加参数(传递给-f选项的参数): /etc/mybackup.conf。这个参数通常与它的参数分开(使用一个空格或其他分隔符), 但这不是必须的, -f/etc/mybackup.conf也是合法的。
- -r依赖于配置。在这个范例中,-r不需要参数,所以它是独立的选项,像-x
- ./foo.txt和./bar.txt是剩余的参数,不与任何选项关联。这些通常用作聚合参数(比如你指定给cp(1)的文件名)或者不需要选项的参数,因为这是程序的预定义格式(就像你传递给文本编辑器的文件名参数,用于打开和显示文本 - 为何还需要一个额外的开关呢?). POSIX®调用它们作为操作对象
让你体会一下为什么getopts很有用: 上面的命令可以像这样读取…
mybackup -xrf /etc/mybackup.conf ./foo.txt ./bar.txt
…使用自己的代码去解析很困难。而getopts可以认出所有常见的选项格式。
选项标识可以有大小写,也可以是数字。甚至是其他可识别字符,但是并不推荐这么做(可用性差而且特殊字符可能会出问题)。
原理
一般你需要调用getopts好几次。每次会使用"下一个"位置参数(和一个可能的附加参数),如果解析成功,会给你返回结果。getopts不会改变位置参数的设定 —— 如果你想要shift掉参数,你必须手工处理:
shift $((OPTIND-1))# now do something with $@
因为getopts在没有要解析的参数剩余时会返回退出状态_FALSE_,所以可以很容易的在while循环使用:
while getopts ...; do ...done
getopts将会解析选项和他们可能的参数。遇到第一个非选项的参数时将会停止解析(一个不以连字符(-)开头的字符串,这不是前面任何一个选项的参数)。同样也会在看到--(双连字符)时停止解析,因为这个含义是。
有用的变量
变量 | 描述 |
---|---|
保存下一个要解析的参数的指针。这就是getopts如何"记住"自己的状态和回调请求。同样可以用于在getopts处理过之后shift掉位置参数。OPTIND初始为1, 如果你想要再次使用getopts解析任何参数时需要重新设置为1 | |
这个变量设置为被getopts发现的选项的参数。同样包含了未知的选项标记 | |
(可选值0或1)表明Bash是否应该显示getopts内置的错误信息。该值在每个shell启动的时候会被初始化为1 - 所以如果你不想看到烦人的信息请务必设置为0! |
getopts同样使用这些变量用于错误报告(they're set to value-combinations which arent possible in normal operation).#括号里面的不会翻译,预留
指明你想要做的
getopts的基本语法是:
getopts OPTSTRING VARNAME [ARGS...]
解释:
选项 | 说明 |
---|---|
OPTSTRING | 告诉getopts期望哪个选项和期望的参数在哪里(参考下面的) |
VARNAME | 告诉getopts哪个shell变量用于选项报告 |
ARG | 告诉getopts将这些解析为附加单词而不是位置参数 |
option-string
option-string告诉getopts期望哪个选项,并且哪个选项必须有参数。语法非常简单 -- 每个字母就是选项名本身,下面这个范例告诉getopts寻找-f, -A和-x:
getopts fAx VARNAME
当你想让getopts对某个选项期望一个参数时,只是在这个选项标记后面放置一个:(冒号)。如果你想让-A期望一个参数(例如变成 -A SOMETHING),只需要:
getopts fA:x VARNAME
如果option-string首个字母是:(冒号),通常是荒谬的,因为没有任何选项在它之前,这种情况下,getopts会切换到"静默错误报告"模式。在产品脚本中,这通常就是你想要的结果(自行抓取错误信息处理,不要被烦人的信息所干扰)。
自定义参数解析
getopts工具会默认解析当前shell或函数的(意味着它解析的是"$@")。
你可以给出你自己的一组参数解析。当附加参数在VARNAME参数之后给出时,getopts并不试图解析这些位置参数。
用这种方式,你可以按照你喜欢的方式解析任何选项,这里是一个数组的范例:
while getopts :f:h opt "${MY_OWN_SET[@]}"; do ...done
不带这些附加参数调用getopts的方式等同于显式的使用"$@"调用:
getopts ... "$@"
错误报告
关于错误报告,getops可以在两种模式下运行:
- 详尽模式
- 安静模式
对于产品脚本我建议使用静默模式,因为这样看起来更专业,你不想看到更多烦人的信息。同样也更容易处理,失败的用例都以更简单的方式显示。
详尽模式
选项 | 说明 |
---|---|
invalid option | VARNAME is set to ? (quersion-mark) and OPTARG is unset |
required argument not found | VARNAME is set to ? (quersion-mark), OPTARG is unset and an error message is printed |
安静模式
选项 | 说明 |
---|---|
invalid option | VARNAME is set to ? (question-mark) and OPTARG is set to the (invalid) option character |
required argument not found | VARNAME is set to : (colon) and OPTARG contains the option-character in question |
开搞
第一个例子
足够说明一些问题!
让我们先看一个非常简单的实例: 只有一个期望的选项(-a),没有任何参数。同样我们用带:(冒号)的option string为了禁用详尽错误显示:
#!/bin/bashwhile getopts ":a" opt; do case $opt in a) echo "-a was triggered!" >&2 ;; \?) echo "Invalid option: -$OPTARG" >&2 ;; esacdone
我把这些内容放到一个文件go_test.sh,就是你接下来看到的范例中的名字。
来让我们做一些测试:
不带任何参数调用
$ ./go_test.sh$
什么都没发生?对的,getopts没看到任何合法或非法的选项(前面有短横线的字母),所以不会触发。
以非选项的参数调用
$ ./go_test.sh /etc/passwd$
还是 — 什么都没发生。非常类似的用例: getopts没看到任何合法或非法的选项(前面有短横线的字母),所以不会触发。
传递给你的脚本的参数当然可以用$1 - ${N}获取。
用选项参数调用
现在让我们触发一下getopts: 提供选项。
首先,来一个非法的:
$ ./go_test.sh -bInvalid option: -b$
和预期的一样,getopts不允许这个选项,像上面说的一样: It placed ? into $opt and the invalid option character (b) into $OPTARG. 我们的用例就验证了这一点。
现在,来一个合法的(-a):
$ ./go_test.sh -a-a was triggered!$
你看到了,这个探测结果运行的很完美。在我们的用例a选项放在了变量$opt中。
当然在调用的时候可以组合有效和无效的选项:
$ ./go_test.sh -a -x -b -c-a was triggered!Invalid option: -xInvalid option: -bInvalid option: -c$
最后,自然也可以多次给出我们的选项:
$ ./go_test.sh -a -a -a -a-a was triggered!-a was triggered!-a was triggered!-a was triggered!$
最后一个例子让我们需要考虑到几点:
- 无效选项并不停止处理过程: 如果你想停掉脚本,你必须自己处理(在正确位置退出)
- 多个相同选项是可行的: 如果你你不允许这么做,你必须自己检测(例如,设置一个变量或其他方式)
一个带参数的选项
让我们把上面的例子扩展一下。只有一点点:
- -a现在需要一个参数-a now takes an argument
- 遇到错误,解析过程以exit 1退出
#!/bin/bashwhile getopts ":a:" opt; do case $opt in a) echo "-a was triggered, Parameter: $OPTARG" >&2 ;; \?) echo "Invalid option: -$OPTARG" >&2 exit 1 ;; :) echo "Option -$OPTARG requires an argument." >&2 exit 1 ;; esacdone
让我们继续做和最后一个范例同样的测试:
不用任何参数调用
$ ./go_test.sh$
像上面一样,什么都没发生。就没触发。
以非选项的参数调用
$ ./go_test.sh /etc/passwd$
非常类似的情况: 没有触发。
带参数选项调用
非法选项:
$ ./go_test.sh -bInvalid option: -b$
和期望的一样,像上面一样,getopts不允许这个选项,和程序算法吻合。
合法选项,但是不带强制要求的参数:
$ ./go_test.sh -aOption -a requires an argument.$
选项OK,但是丢掉了一个参数。
让我们提供这个参数:
$ ./go_test.sh -a /etc/passwd-a was triggered, Parameter: /etc/passwd$
参考资料
- Internal:
- Internal:
- Internal:
- POSIX and