getopt与getopts的用法
前言
先说结论:不推荐使用。更方便的配置选项的方式是使用Python的argparse库,比这种方式要舒适得多。如果你一定要用Shell脚本配置选项,那么请做好受苦的准备——往下看吧。
短选项
使用getopts
命令设置脚本的短选项。这是一个 POSIX 标准命令。getopts
的语法为:getopts "[:]选项列表" 选项名变量 [参数列表]
参数列表不需要明确指定,默认使用$@
。
选项均为短选项,列表没有分隔符,所有选项全部连在一起写出。如果一个选项后带:
符号,则意味着这个选项需要接受参数。
每次执行getopts
时,它都会获取命令行中给出的下一个选项及其参数(在本质上,这是通过$OPTIND
变量的值分析的),将选项的字符保存在$选项名变量
中,将选项或选项的参数的位置保存在$OPTIND
变量中(对于有参选项来说,是参数的位置,对于无参选项来说,就是选项本身的位置),将选项的参数值保存在$OPTARG
变量中(对于无参选项来说,这个变量总是会被置空)。
如果一个选项是有参选项,那么千万不能在使用时忘记给出参数,否则会导致非常恶性的 BUG(选项会将下一个选项视为参数),只有当有参选项位于末尾时,getopts
才会检测到参数缺失的情况。
开头的:
意味着关闭错误回显,在没有关闭错误回显的情况下,如果用户的行为不合法(位于末尾的缺少参数的选项,或者并不存在于列表中的选项),那么getopts
会给出警告,但是这不会影响脚本的执行;如果关闭了错误回显,那么getopts
就不会给出警告,而且还会改变某些行为。
无论是否关闭了错误回显,在遇到不存在于列表中的选项时,getopts
都会将$选项名变量
设为?
,并将选项视为无参选项,选项位置保存在$OPTIND
变量中,将$OPTARG
变量置空;**但是如果关闭了错误回显,getopts
还会将选项名赋给$OPTARG
**。
再次强调,如果一个选项缺少参数,那么只有在这个选项位于末尾时getopts
才能检测到参数缺失的情况。如果没有关闭错误回显,那么对于缺失参数的选项,getopts
会将$选项名变量
设为?
,并将选项位置保存在$OPTIND
变量中,将$OPTARG
变量置空;如果关闭了错误回显,那么getopts
会将$选项名变量
设为:
,并将选项位置保存在$OPTIND
变量中,将$OPTARG
变量置空。
当getopts
已经遍历完命令行中给出的选项时,如果再次执行getopts
,那么getopts
会将$选项名变量
设为?
,对$OPTIND
变量不进行任何操作(保留最后一个选项或选项参数的位置),将$OPTARG
变量置空,然后返回一个非零的值。
为了方便使用,getopts
段一般放在脚本的开头位置,而且会结合上一条特性,使用while
循环和case
条件判断简化遍历提取选项的过程:
1 | while (getopts "选项列表" OPTNAME) # 遍历并提取所有的选项 |
shift $((OPTIND-1))
在此处的作用是移除所有的选项及其参数,将位置参数重新复位到$1
的位置。这一步操作是一定要有的,否则$1
指向的是第一个选项,换句话说,第一个选项会被误认为是第一个位置参数。
切记位置参数在使用getopts
时只能放在所有的选项和选项参数之后,否则getopts
会不起作用(无法设置任何选项)。
长选项
使用getopt
命令设置脚本的长选项,注意这不是一个 POSIX 标准命令,它来自于utils-linux
软件包。getopt
的语法为:getopt -o 短选项列表 -l 长选项列表 参数列表
它还支持以下选项:
1 | -a 允许长选项使用单横线开头 |
参数列表必须明确指定,一般使用$@
。
短选项列表没有分隔符,所有选项全部连在一起写出。长选项列表以,
作为分隔符。
如果一个选项后带:
符号,表示这个选项是有参选项,需要接受参数;如果一个选项后带::
符号,表示这个选项是可选参数选项,参数可以省略,省略的参数会被置空。
getopt
会对命令行中给出的选项和参数进行整理,整理成如下形式:选项与选项参数 -- 位置参数
选项与选项参数按在命令行中给出的选项顺序排列。
排列后,脚本应当先使用set --
命令重设位置参数,然后通过$1
变量按顺序遍历选项名及其类型。
如果是无参选项,那么获取后应当执行shift
偏移一位,将选项移除。如果是有参选项或参数可选选项,那么获取后应当执行shift 2
偏移两位,将选项及其参数移除。
最后,在检测到--
时,执行shift
偏移一位并结束选项遍历。
这个过程通常要用到while
循环和case
条件判断:
1 | eval set -- $(getopt -o 短选项列表 -l 长选项列表 "$@") |
对于getopt
来说,位置参数的位置较为松散,可以放在所有的选项之前,也可以放在所有的选项之后。