一般的 shell 脚本都不会有太复杂的命令行参数,这时候使用$n
获取位置参数就足够了。
但是,有些时候需要解析一些比较复杂的命令行参数,这时候就需要 shift 和 getopts 了。
shift
shift
是 shell 的一个内建命令,常用于位置参数的解析;
shift 将所有位置参数(除$0
)往前移动一个单位,即丢弃最前边的参数;
如:shift 3
,往前移动三个位置、不带参数的shift
则相当于shift 1
。
最简单的用法,依次获取每个位置参数(当然,这只是演示一下 shift 的用法)
1
2
3
4
5
6
7
8
#!/bin/bash
echo "文件名: $0"
while [ $# -gt 0 ]; do
echo "参数$((++i)): $1"
shift
done
1
2
3
4
5
6
$ ./test.sh a b c d
文件名: ./test.sh
参数1: a
参数2: b
参数3: c
参数4: d
getopts
除了使用 shift,我们还可以使用 getopts 内建命令,它主要是模仿 C 库中的 getopt() 函数。
语法:getopts optstring optname [arguments ...]
变量$OPTIND
:选项所在的索引值,初始值为 1,它会自动递增;
变量$OPTARG
:选项所附带的参数(如果有的话)。
optstring
是选项字符串,用来定义如何处理选项;
optname
是一个变量,用来存储捕获到的选项;
如果没有 arguments
,则从当前的位置参数解析;
如果有 arguments
,则从给定的 arguments
解析。
optstring
格式:
- 如果选项后面没有
:
号,说明该选项后没有参数值; - 如果选项后面带有
:
号,说明该选项后需要提供参数,参数值存储在$OPTARG
。
当 getopts 遇到未知选项、选项缺少参数的情况时:
- 如果 optstring 以
:
开头,getopts 将不会输出默认的出错信息,而是交给我们自己来处理。并且,遇到未知选项时将optname
设为?
,遇到缺少参数时将optname
设为:
; - 如果 optstring 不以
:
开头,getopts 将输出默认的出错信息,并且不区分未知选项和缺少参数的情况,统一将optname
设为?
。
简单例子,演示了如何使用 getopts:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/bin/bash
if [ $# -eq 0 ]; then
echo "Usage ..." 1>&2 # 用法介绍
exit 1
fi
while getopts ":s:p:m:k:b:l:uvh" OPT; do
case $OPT in
s) server_ip=$OPTARG;;
p) server_port=$OPTARG;;
m) method=$OPTARG;;
k) passwd=$OPTARG;;
b) bind_ip=$OPTARG;;
l) listen_port=$OPTARG;;
u) udp_relay=1;;
v) verbose=1;;
h) help=1;;
:) # 缺少参数
echo "missing argument to '-$OPTARG'" 1>&2
exit 1
;;
\?) # 未知选项
echo "unknown option '-$OPTARG'" 1>&2
exit 1
;;
esac
done
# 程序主逻辑 ...
1
2
3
4
5
6
7
8
9
10
$ ./test.sh -s
missing argument to '-s'
$ ./test.sh
Usage ...
$ ./test.sh -y
unknown option '-y'
$ ./test.sh -s server -p 5555 -m rc4-md5 -k password -b 0.0.0.0 -l 1080 -u -v
在函数中使用 getopts
因为$OPTIND
每次都会自增,因此调用一次函数后,$OPTIND
就不再是 1 了,再调用函数就会无法解析;
为了解决这个问题,可以在函数中定义一个与$OPTIND
同名的 local 变量,并且初始值设为 1,就可以了。