Bash中的script是强大的,但如果想让写出的脚本更加实用、灵活,不能简单的堆砌命令,势必要加上一些命令行参数。命令行参数除了实际的操作对象以外,还可能是一些选项(通常是用-开头的),如果还是用$1、$2这样的方式一个一个的判断参数到底是不是选项、是哪个选项就太低效了,更好的方式是用getopts,先看简单的例子:
#!/bin/bash
while getopts 'd:Dm:f:t:' OPT; do
case $OPT in
d)
DEL_DAYS="$OPTARG";;
D)
DEL_ORIGINAL='yes';;
f)
DIR_FROM="$OPTARG";;
m)
MAILDIR_NAME="$OPTARG";;
t)
DIR_TO="$OPTARG";;
?)
echo "Usage: `basename $0` [options] filename"
esac
done
shift $(($OPTIND - 1))
getopts后面的字符串就是可以使用的选项列表,每个字母代表一个选项,后面带:
的意味着选项除了定义本身之外,还会带上一个参数作为选项的值,比如d:
在实际的使用中就会对应-d 30
,选项的值就是30;getopts字符串中没有跟随:
的是开关型选项,不需要再指定值,相当于true/false,只要带了这个参数就是true。如果命令行中包含了没有在getopts列表中的选项,会有警告信息,如果在整个getopts字符串前面也加上个:
,就能消除警告信息了。
使用getopts识别出各个选项之后,就可以配合case来进行相应的操作了。操作中有两个相对固定的“常量”,一个是OPTARG
,用来取当前选项的值,另外一个是OPTIND
,代表当前选项在参数列表中的位移。注意case中的最后一个选择──?
,代表这如果出现了不认识的选项,所进行的操作。
选项参数识别完成之后,如果要取剩余的其它命令行参数,可以使用shift
把选项参数抹去,就像例子里面的那样,对整个参数列表进行左移操作,最左边的参数就丢失了(已经用case判断并进行了处理,不再需要了),位移的长度正好是刚才case循环完毕之后的OPTIND - 1
,因为参数从1开始编号,选项处理完毕之后,正好指向剩余其它参数的第一个。在这里还要知道,getopts在处理参数的时候,处理一个开关型选项,OPTIND加1,处理一个带值的选项参数,OPTIND则会加2。
最后,真正需要处理的参数就是$1~$#了,可以用for循环依次处理。
使用getopts处理参数虽然是更加方便了,但仍然有两个小小的局限:
- 选项参数的格式必须是
-d val
,而不能是中间没有空格的-dval
。 - 所有选项参数必须写在其它参数的前面,因为getopts是从命令行前面开始处理,遇到非
-
开头的参数,或者选项参数结束标记--
就中止了,如果中间遇到非选项的命令行参数,后面的选项参数就都取不到了。
参考
- [Parsing arguments for your shell script](http://www.linux.com/feature/118031)
最好用的,還是 getopt 這個指令了(getopts 是 bash 的功能,而 getopt 則是獨立的一個程式)
倒是找到了getopt的一篇文章,可惜没有看太明白,似乎作者也没有特意说明它的优点。
谢了,正好找这方面的资料 🙂