在Linuxbashshell中,有一个内置的getopts命令可以处理以'-'开头的选项参数。本文通过多个实例详细讲解getopts命令的使用。getopts命令简介在bashshell上执行命令,经常会使用一些选项参数来指定不同的操作。例如ls命令的-l、-a选项等。我们在编写shell脚本的时候,也可以自定义一些选项参数,使用bash内置的getopts命令来解析选项参数。查看manbash里面对getopts内部设置命令的英文说明如下:getoptsoptstringname[args]getopts由shell过程用来解析位置参数。optstring包含要识别的选项字符;如果一个字符后跟一个冒号,则该选项应该有一个参数,应该用空格与它分开。冒号和问号字符不能用作选项字符。每次调用时,getopts将下一个选项放入shell变量名中,如果不存在则初始化名称,并将下一个要处理的参数的索引放入变量OPTIND中。每次调用shell或shell脚本时,OPTIND都被初始化为1。当一个选项需要一个参数时,getopts将该参数放入变量OPTARG中。shell不会自动重置OPTIND;如果要使用一组新参数,则必须在同一个shell调用中多次调用getopts之间手动重置它.当遇到选项结尾时,getopts以大于零的返回值退出。OPTIND设置为第一个非选项参数的索引,name设置为?。getopts通常解析位置参数,但如果args中给出了更多参数,则getopts解析这些参数。getopts可以通过两种方式报告错误。如果optstring的第一个字符是冒号,则使用静默错误报告。在正常操作中,遇到无效选项或缺少选项参数时会打印诊断消息。如果变量OPTERR设置为0,则不会显示错误消息,即使optstring的第一个字符不是冒号。如果看到无效选项,getopts会放置?进入名称,如果不是静默,则打印一条错误消息并取消设置OPTARG。如果getopts是静默的,找到的选项字符被放置在OPTARG中并且不打印诊断消息。如果没有找到所需的参数,并且getopts不是静默的,一个问号(?)放在名称中,OPTARG未设置,并打印诊断消息。如果getopts是静默的,那么在名称中放置一个冒号(:),并将OPTARG设置为找到的选项字符。注意:getopts是bash的内置命令对于bash的内置命令,不能使用man命令查看它们的帮助说明。要用help命令查看。也可以在manbash中搜索命令名,查看对应的说明。$mangetoptsNomanualentryforgetopts$helpgetoptsgetopts:getoptsoptstringname[arg]解析选项参数。可以看到,mangetopts提示找不到getopts命令的描述,但是helpgetopts打印了它的描述。另外还有一个getopt外部命令,也可以解析命令选项。名字比getopts少一个s,用法也不一样。不要混淆这两个命令。getoptsoptstringname[args]根据getoptsoptstringname[args]的命令格式,解释getopts命令各参数的含义。optstring参数指定支持的选项参数列表,每个字符对应一个选项。如果该字符后跟一个冒号:,则该选项在输入时应后跟一个参数,以空格分隔。冒号:还有问号?不能用作选项。例如,有效的optstring参数值为“hi:”。然后-h是一个选项;-i也是一个选项。由于i后面跟了一个冒号:,所以在输入-i选项的时候还带了一个参数,比如-iinsert等。如果在实际执行的时候-i后面没有带参数,getopts命令会报错。注意:optstring参数的选项列表中不包含-字符,但实际输入option参数时,getopts命令要求option参数以-开头,否则会报错。在上面的示例中,-h是一个选项,但h不是有效选项。name参数用于保存解析后的选项名称。每次调用getopts命令时,它只解析一个选项并将解析的值存储在name变量中。解析值不包含-字符。例如,解析-h选项后,name变量的值为字符h。变量的名字可以不要求是名字字符串,也可以是其他合法的变量名,比如opt、arg等。如果要解析多个选项,需要在一个周期内多次执行getopts命令while或for循环逐一解析参数选项,直到解析完成。解析后getopts命令会返回false,从而退出循环。如果提供的选项不在optstring指定的列表中,name的值将设置为问号?。但是getopts命令还是返回true,没有报错。[args]是一个可选参数,用于指定选项参数的来源。默认情况下,getopts命令解析由位置参数提供的参数,例如$1、$2、...等。如果提供了args参数,则选项参数将从args而非位置参数中解析。也就是说,在shell脚本中直接执行getopts命令时,默认解析的option参数为脚本执行时提供的参数。比如有testgetopts.sh脚本,执行./testgetopts.sh-a-b命令,getopts会解析-a和-b选项。如果getopts命令是在函数内部执行的,那么它解析出的option参数就是调用函数时提供的参数。比如有一个test_getopts函数,它调用getopts命令,然后执行test_getopts-a-b语句,getopts命令会解析-a,-b选项。如果提供了args参数,则getopts命令会解析包含在args参数中的选项。例如,执行args="-a-b";getopts"ab"opt$args语句,getopts命令解析由args变量指定的"-a-b"字符串。OPTARG是getopts命令使用的全局变量,在冒号选项后保存解析后的参数值。比如解析上面提到的-iinsert选项,OPTARG的值为insert。OPTIND是getopts命令使用的全局变量,保存了下一个要解析的参数索引。当启动一个新的shell时,OPTIND的默认值为1,调用一次getopts命令时OPTIND的值加1。如果在带冒号的选项后提供参数,则OPTIND的值增加2。当getopts命令解析完所有参数时,shell不会自动将OPTIND重置为1。如果要解析不同的选项参数同样的shell脚本,需要手动给OPTIND赋值1,否则会解析出意想不到的选项。后面会以一个testgetopts.sh脚本为例进行说明。getopts命令的返回值在manbash中解释如下:如果找到指定或未指定的选项,则getopts返回true。如果遇到选项结尾或发生错误,则返回false。您可以看到,即使提供了不受支持的选项,getopts命令也会返回true。getopts在解析完所有选项时返回false,在出错时返回false。有几种情况会遇到错误:optiondoesnotstartwith-带冒号的选项后面需要提供参数,但是没有提供参数testgetopts.sh脚本下面以一个testgetopts.sh脚本为例为了说明getopts命令的用法,其内容如下:#!/bin/bashfunctiontest_getopts_ab(){localopt_abwhilegetopts"ab"opt_ab;doecho$FUNCNAME:$OPTIND:$opt_abdone}echoBeforeCalltest_getopts_ab:OPTIND:$OPTIND++test_getopts_ab"-a""-b"echo调用后test_getopts_ab:OPTIND:$OPTIND--whilegetopts"ef"opt_ef;doecho$OPTIND:$opt_efdoneOPTIND=1echoResetOPTINDto:$OPTINDnumber=6;whilegetopts"s:g"opt_sg;做case$opt_sging)echo$number;;s)number=$OPTARG;;?)回声“未知选项:$opt_sg”;;esacdone该脚本首先调用test_getopts_ab函数中的getopts命令解析传入的“-a”“-b”选项。然后调用getopts命令解析执行脚本时传入的命令行选项参数。最后重新设置OPTIND的值,重新解析命令行选项参数。就拿getopts"ab"opt_ab语句来说,"ab"对应上面提到的optstring参数,它支持的选项有-a和-b。opt_ab对应上面提到的name变量名,保存解析后的option,不加-字符。执行这个脚本,结果如??下:$./testgetopts.sh-s7-g-fBeforeCalltest_getopts_ab:OPTIND:1++test_getopts_ab:2:atest_getopts_ab:3:bAfterCalltest_getopts_ab:OPTIND:3--./testgetopts.sh:非法选项--g4:?5:fSetOPTINDto:17./testgetopts.sh:非法选项--funknownoption:?可以看出,test_getopts_ab函数解析option参数后,函数外打印的OPTIND值为3。再次调用getopts命令后,OPTIND的值不是从1开始,而是从3开始。获取到传递给testgetopts.sh的第三个参数-g,跳过前面两个参数-s7。这不是预期的结果。通常,期望从第一个选项参数开始解析。由于getopts"ef"opt_ef语句不支持-g选项,因此会打印一条错误消息并为opt_ef分配一个问号?。手动将OPTIND值重置为1后,getopts"s:g"opt_sg可以从第一个选项参数开始解析。先处理-s7选项,getopts将7赋值给OPTARG,然后将OPTARG的值赋值给脚本中的number变量。然后它继续处理-g选项,打印出number变量的值。最后处理-f选项,不支持该选项,opt_sg的值设置为问号?,并打印信息“unknownoption”。处理完所有选项后,getopts返回false,退出while循环。错误判断当getopts命令处理完option参数或遇到错误时,会返回false,不能通过判断返回值来确认是否遇到错误。在while或for循环中调用getopts时,可以通过OPTIND的值来判断getopts是否遇到了错误。如果OPTIND的值减去1不等于传入的参数个数,那么就会遇到错误,提前退出循环。getopts处理optionarguments时,OPTIND的值从1开始递增。处理完所有参数后,OPTIND指向最后一个参数,相当于所有参数个数加1。所以OPTIND的值减1应该等于传入的参数个数,bash的$#表达式可以得到传入的参数个数,如果两个值不相等,说明getopts还没有解析完option参数,也就是说,它遇到了错误并提前退出了循环。假设有一个test.sh脚本,内容如下:#!/bin/bashwhilegetopts"abcd"arg;做echo$OPTIND:$argdoneechoOPTIND:$OPTIND。\$\#:$#if["$(($OPTIND-1))"!="$#"];然后echoErroroccurs.fi传入不同的option参数,脚本执行结果如下:$./test.sh-a-b-c2:a3:b4:cOPTIND:4.$#:3$。/test.sh-a-b2:a3:bOPTIND:3.$#:2$./test.sh-ab2:aOPTIND:2.$#:2发生错误。OK看到了,正常遇到选项结束时,OPTIND变量的值是选项个数加1,遇到错误时,OPTIND变量的值不是选项个数加1。所以当OPTIND变量的值减1不等于$#时,就说明遇到了错误。通过source多次执行脚本对OPTIND的影响通过source命令调用的脚本是在当前shell下运行的。由于shell不会自动重新设置OPTIND的值,如果要调用的脚本使用getopts命令解析option参数,在每次调用getopts之前,必须手动重新设置OPTIND为1,否则的值OPTIND不会从1增加,你会得到意想不到的选项参数值。假设有一个test.sh脚本,它的内容如下:#!/bin/bashecho\$\#:$#,\$\@:$@,OPTIND:$OPTINDgetopts"abc"optecho$?,$opt分别没有使用source命令和使用source命令执行脚本,结果如??下:$./test.sh-a$#:1,$@:-a,OPTIND:10,a$./test.sh-b$#:1,$@:-b,OPTIND:10,b$source./test.sh-a$#:1,$@:-a,OPTIND:10,a$source./test.sh-b$#:1,$@:-b,OPTIND:21,?可以看到执行./test.sh-a命令和./test.sh-b命令的输出结果都是正常的。执行source./test.sh-a命令的结果也是正常的。但是再执行source./test.sh-b命令,在调用getopts之前,打印出OPTIND的值为2,得到第二个option参数。由于没有提供第二个option参数,所以得到的option参数的值为问号?,使用$?得到的getopts命令的返回值?为1,执行时报错。即如果一个脚本使用了getopts命令,而脚本需要用source命令执行,则脚本需要手动将OPTIND变量的值设置为1,否则会遇到上述异常。当我们自己写一个脚本,在.bashrc文件中配置alias使用source命令执行脚本,例如如下设置:aliasc='sourcequickcd.sh'当quickcd.sh脚本使用getopts命令时,并且没有ResetOPTIND的值,然后多次执行c命令,就会遇到上面描述的异常。
