写shell脚本的时候,通过while、case、shift来设计脚本的命令行选项是一件比较麻烦的事,因为Unix命令行的选项和参数自由度很高,支持短选项和长选项,参数可能是可选的,选项顺序可能是无所谓的,等等。
bash下的getopt命令可以解析命令行的选项和参数,将散乱、自由的命令行选项和参数进行改造,得到一个完整的、规范化的参数列表,这样再使用while、case和shift进行处理就简单的太多了。
getopt有不同的版本,本文介绍的是它的增强版(enhanced),相比传统的getopt(也称为兼容版本的getopt),它提供了引号保护的能力。另外,除了不同版本的getopt,bash还有一个内置命令getopts(注意,有个尾随的字符s),也用来解析命令行选项,但只能解析短选项。
要验证安装的getopt是增强版的还是传统版的,使用判断即可。如果它什么都不输出,则是增强版,此时它的退出状态码为4。如果输出"--",则是传统版的getopt,此时它的退出状态码为0。如果想在脚本中进行版本检查,可以参考如下代码:
在学习getopt如何使用之前,必须先知道命令行的一些常识。这些,都可以通过getopt来实现,但有些实现起来可能会比较复杂。
1.区分option、parameter、argument、option argument和non-option parameter
parameter和argument都表示参数,前者通常表示独立性的参数,后者通常表示依赖于其它实体的参数。parameter的含义更广,argument可以看作parameter的一种。
例如,定义函数时,函数的参数x和y称为parameter。调用函数并传递参数时,中的arg1和arg2都是依赖于函数的,称为argument更合适,当然也可以称为更广泛的parameter。
再例如,一个命令行:
粗分的话,、、、、都可以称为parameter。细分的话:
- "-z -c -f"称为选项,即option
- a.tar.gz是选项"-f"的选项参数(传递给选项的参数),依赖于选项,称为argument更合适,更严格的称呼是option argument
- /etc/pki既不属于选项,也不属于某个选项的参数,它称为非选项类型的参数,对应的名称为non-option parameter
本文要介绍的是getopt,所以只考虑命令行参数的情况。
2.短选项和长选项以及它们的"潜规则"
Linux中绝大多数命令都提供了短选项和长选项。一般来说,短选项是只使用一个"-"开头,选项部分只使用一个字符,长选项是使用两个短横线(即"--")开头的。
例如"-a"是短选项,"--append"是长选项。
一般来说,选项的顺序是无所谓的,但并非绝对如此,有时候某些选项必须放在前面,必须放在某些选项的前面、后面。
一般来说,短选项:
- 可以通过一个短横线"-"将多个短选项连接在一起,但如果连在一起的短选项有参数的话,则必须作为串联的最后一个字符。
例如"-avz"其实会被解析为"-a -v -z",串联了多个短选项,但"-f"选项有参数a.tar.gz,所以它必须作为串联选项的最后一个字符。
- 短选项的参数可以和选项名称连在一起,也可以是用空白分隔。例如和是等价的,数值3都是"-n"选项的参数值。
- 如果某个短选项的参数是可选的,那么它的参数必须紧跟在选项名后面,不能使用空格分开。至于为什么,见下面的第3项。
一般来说,长选项:
- 可以使用等号或空白连接两种方式提供选项参数。例如或。
- 如果某个长选项的参数是可选的,那么它的参数必须使用"="连接。至于为什么,见下面的第3项。
- 长选项一般可以缩写,只要不产生歧义即可。
例如,ls命令,以"a"开头的长选项有3个。
如果想要指定,可以缩写为;如果想要指定,可以缩写为。如果只缩写为"--a",bash将给出错误提示,长选项出现歧义:
3.不带参数的选项、可选参数的选项和带参数的选项
有不同类型的命令行选项,这些选项可能不需要参数,也可能参数是可选的,也可能是强制要求参数的。
前面说了,如果某个选项的参数是可选的,那么它的参数必须不能使用空格将参数和选项分开。如果使用空格分隔,则无法判断它的下一个元素是该选项的参数还是非选项类型的参数。
例如,和选项的参数是可选的,要向这两个选项提供参数,必须写成、,如果写成、,那么命令行将无法判断这个FILE是提供给选项的参数,还是非选项类型的参数。
一般来说,使用可选参数的情况非常少,至少我目前回忆不起来这样的命令(mysql -p选项是一个)。
4.使用"--"将选项(及它们的选项参数)与非选项类型参数进行分隔
unix的命令行中,总是可以在非选项类型的参数之前加上"--",表示选项和选项参数到此为止,后面的都是非选项类型的参数。
例如:
分别表示3和"1 3"是seq的非选项类型参数,而"--"前面的一定是选项或选项参数。
5.命令行参数中的短横线开头的并不一定总是短选项,也可能是负数参数
例如seq命令:
其中-5和-1都是负数非选项类型的参数。
6.选项的依赖性和互斥性
有些命令的选项是有依赖性和互斥性的。比如某个选项要和另一个选项一起使用,某个选项不能和另一个选项一起使用。
例如,只有在使用了的前提下才能使用,否则就应该报错。
7.模式化(模块化)类型的选项
很多unix命令都将选项进行模块化设计。例如ip命令,address模式、route模式、link模式等等。
8.其他特性的选项
有些命令还有比较个性化的选项。
比如head命令,选项,即可以指定为,也可以指定为或。
再比如有的命令支持逻辑运算,例如find命令的-a、-o选项。
bash的getopt命令经常用在shell脚本内部或函数内部,用来解析脚本执行或函数执行时传递的选项、参数。
下面都以命令行为例解释getopt是如何解析参数的,但用来解析函数参数是一样的。
下面这个是最常用的getopt解析方式(有这个命令就够了)。如果要了解getopt更完整的语法,见man getopt。
其中:
getopt通过"-o"选项收集命令行传递的短选项和它们对应的参数。关于SHORT_OPTIONS的格式见下一小节。
getopt通过"-l"选项收集命令行传递的长选项和它们对应的参数。可能从别人的脚本中经常看到"--long",是等价的,前文已经解释过,长选项只要不产生歧义,是可以进行缩写的。关于LONG_OPTIONS的格式见下一小节。
getopt使用"-o"或"-l"解析短、长选项和参数时,将会对每个解析到的选项、参数进行输出,然后不断放进一个字符串中。这个字符串的内容就是完整的、规范化的选项和参数。
getopt使用"-o"选项解析短选项时:
- 多个短选项可以连在一起
- 如果某个要解析的选项需要一个参数,则在选项名后面跟一个冒号
- 如果某个要解析的选项的参数可选,则在选项名后面跟两个冒号
- 例如,中,将解析为,arg_b是-b选项必须的,arg_c是-c选项可选的参数,"-a"选项无需参数
getopt使用"-l"选项解析长选项时:
- 可以一次性指定多个选项名称,需要使用逗号分隔它们
- 可以多次使用-l选项,多次解析长选项
- 如果某个要解析的选项需要一个参数,则在选项名后面跟一个冒号
- 如果某个要解析的选项的参数可选,则在选项名后面跟两个冒号
- 例如,中,将解析为,其中arg_add是选项必须的,选项的参数arg_rem是可选的,无需参数
如果解析的是带参数的选项,则getopt生成的字符串中,会将选项的参数值作为该选项的下一个参数。如果解析的是可选参数的选项,如果为该选项设置了参数,则会将这个参数放在选项的下一个参数位置,如果没有为该选项设置参数,则会生成一个用引号包围的空字符串作为选项的下一个参数。
getopt解析完选项和选项的参数后,将解析非选项类型的参数(non-option parameter)。getopt为了让非选项类型的参数和选项、选项参数区分开,将在解析第一个非选项类型参数时加上一个"--"到字符串中,表示选项和选项参数到此结束,然后将所有的非选项类型参数放在这个"--"参数之后。
默认情况下,该加强版本的getopt会将所有参数值(包括选项参数、非选项类型的参数)使用引号进行包围,以便保护空白字符和特殊字符。如果是兼容版本的getopt,则不会用引号保护,所以会破坏参数解析。
看后面的示例就很容易理解了。
例如在脚本test.sh中,下面的getopt的结果保存到变量parameters中,然后输出getopt解析完成后得到的完整参数列表。
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.mushiming.com/mjsbk/1196.html