有时候,我们希望给脚本提供选项,以改变脚本的行为。那么如何很好的解析选项呢?其实有三种办法
- 使用位置参数进行解析。
- 使用shell的内置命令 getopts 进行解析。
- 使用 Linux 的 getopt 命令进行解析。
如果选项比较简单,完全可以使用位置参数进行解析,如果你想正规一点,那就用 shell 的内置 getopts 命令进行解析。
但是 shell 内置命令 getopts 不支持长选项,而且命令格式必须先输入选项,再输入命令其他的参数。如果你有这种困惑,那么就使用 Linux 的 getopt 命令解析。
getopt 命令可能需要安装,但是 getopts 是 shell 内置,因此没有特殊要求,首选 getopts。
getopts 语法如下
getopts optstring name [arg]
参数 optstring 包含了需要被识别的选项,例如有两个选项 -a
和 -b
,所以 optstring 的值为 ab
,如果选项后面需要带参数,那么 optstring 中对应的选项字母后面必须跟一个冒号,例如 -a
选项必须要有参数,因此 optstring 的值为 a:b
,其中 a 后面有一个冒号,表示选项 a 必须有参数。
每次调用 getopts 命令,会把解析的选项名赋值给参数变量 name,但是这个选项名不包括前面选项前面的 -
,并且如果选项有参数值,那么这个值会赋值给 OPTARG
变量。因此,如果我们想解析所有选项,必须循环调用 getopts 命令。
每次调用 getopts 还会把下一个要解析的选项的索引值,赋给 OPTIND
变量。OPTIND
变量的初始值为1,每调用一个命令,就会加1。因此,当所有选项解析完毕后,OPTIND
的值比选项的数量多1.
我们还是以上面的例子中的命令为例,如果要解析如下命令
opt_parse.sh -a a_value -b file1 file2
那么使用 getopts 解析选项的脚本如下
#!/bin/bash
# 解析选项
while getopts a:b opt; do
case $opt in
a)
echo "find option -a, and argument is $OPTARG"
;;
b)
echo "find option -b, no argument"
;;
esac
done
# 解析命令参数
shift $((OPTIND - 1))
while [[ -n "$1" ]]; do
echo "find argument: $1"
shift
done
getopts 命令只能解析选项以及选项的参数,如果命令还有更多的参数,必须通过 shift 来位移位置参数,再解析剩余的参数。
如果我们使用了未知的选项,那么 getopts 会报错,例如执行下面的命令
opt_parse.sh -d
报错如下
opt_parse.sh: 非法选项 -- d
如果我们不需要这样的报错,那么getopts 语法中的 optstring 需要以冒号开头,这样就能抑制这样的错误信息。
如果我们不要这样的报错,那么一般会选择提示脚本的使用语法,如下
#!/bin/bash
function usage
{
echo "$(basename $0) -a arg -b file..."
}
# 解析选项
# :a:b 是以冒号开头,会抑制错误选项的信息
while getopts :a:b opt; do
case $opt in
a)
echo "find option -a, and argument is $OPTARG"
;;
b)
echo "find option -b, no argument"
;;
*)
# 无效选项,就提示脚本的使用方式
usage >&2
;;
esac
done
# 解析命令参数
shift $((OPTIND - 1))
while [[ -n "$1" ]]; do
echo "find argument: $1"
shift
done