Shell命令行参数解析

日常使用的大多数命令行工具,都会提供一些选项。例如当我们想实现类似下面的命令行:

1
./build-image -v 3.0.0

就需要对命令行参数进行解析,命令行选项解析工具 getoptsgetopt

getopts与getopt

  1. getoptsShell 内建命令,getopt 是一个独立外部工具

  2. getopt 的语法比getopts要复杂

  3. getopts 不支持长参数(如:--version ),getopt 支持

  4. getopts 不会重排所有参数的顺序,getopt 会重排参数顺序

getopts

我们将 getopts 放在 while 循环中。getopts 解析到参数时,会返回 TRUE;否则返回 FALSE,用以结束循环。

getopts停止解析并返回 FALSE的情况:

  • getopts读入不以-开始的字符串;
  • getopts读入连续的两个-(i.e. --)。

getopts不支持两个连字符引导的选项,而是将两个连续的连字符作为「选项结束的标志」。

getopts 的格式

1
getopts opts varname [args...]
  • opts 选项列表
  • varname 用于保存 getopts 解析到的选项的变量名(参数值保存在 OPTARG 里)
  • args... 是可选的,默认是 $@(Shell 脚本的全部参数)。

  • OPTIND: getopts在解析传入 Shell 脚本的参数时,并不会执行 shift 操作,而是通过变量 OPTIND 来记住接下来要解析的参数的位置。

  • OPTARG: getopts在解析到选项的参数时,就会将参数保存在 OPTARG 变量当中;如果 getopts 遇到不合法的选项,择把选项本身保存在 OPTARG 当中。

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#test.sh
while getopts ":t:v:p" arg
do
case $arg in
t)
export BUILD_TYPE=$OPTARG
;;
v)
export TAG=$OPTARG
;;
p)
export PUSH_IMAGES=true
;;
:)
echo "-$OPTARG need a parameter"
exit 1
;;
?)
echo "Invalid option -$OPTARG"
exit 1
;;
esac
done

上面的例子中 getopts “:t:v:p” arg,首位的:表示不打印错误信息,选项后的:表示该选项接收一个参数。也就是选项 v 和 t 都需要参数。

1
2
$ ./test.sh -t
-t need a parameter

在遇到:时,getopts尝试解析参数失败了。此时,getopts:保存在$arg当中,而后将参数名t保存在$OPTARG中。

getopt

上面提到getopt 是一个独立外部工具,但它在大多Linux的发行版中都有,如果没有,可以从getopt官网上下载安装。
getopt是通过将参数规范化来帮助我们处理。

getopt 的格式

1
getopt -o opts --long longopts -n string -- string
  • -o-options:短选项,两个冒号表示该选项有一个可选参数,可选参数必须紧贴选项,如ab:c::,表示可接受的短选项为-a -b -c,其中-a选项不接参数,-b选项后必须接参数,-c选项的参数可选。
  • -l--long:长选项,用逗号分开,冒号的意义同短选项。
  • -n: 出错时的提示信息。
  • --:举一个例子来理解:我们要创建一个名为 “-f”的目录怎么办?mkdir -f #不成功,因为-f会被mkdir当作选项来解析,这时就可以使用 mkdir – -f 这样-f就不会被作为选项。

实例

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#test.sh
TEMP=`getopt -o ab:c:: --long along,blong:,clong:: -n 'example.bash' -- "$@"`
if [ $? != 0 ]; then
echo "Terminating..."
exit 1
fi
#set 将规范化后的命令行参数分配至位置参数($1,$2,...)
eval set -- "$TEMP"
echo "$TEMP"
while true
do
case "$1" in
-a|--along)
echo "Option a"
shift
;;
-b|--blong)
echo "Option b, argument $2"
shift 2
;;
-c|--clong)
case "$2" in
"")
echo "Option c, no argument"
shift 2
;;
*)
echo "Option c, argument $2"
shift 2
;;
esac
;;
--)
shift
break
;;
*)
echo "Internal error!"
exit 1
;;
esac
done
#处理剩余的参数
for arg in $@
do
echo "processing $arg"
done

-c选项的参数是可选的,所以参数必须紧贴选项,或者使用=连接参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ ./test.sh -b 123 -a -c456 arg1 arg2
--b '123' -a --c '456' -- 'file1' 'file2'
Option b, argument 123
Option a
Option c, argument 456
processing arg1
processing arg2
$ ./test.sh -b 123 -a -c arg1 arg2
--b '123' -a --c '' -- 'file1' 'file2'
Option b, argument 123
Option a
Option c, no argument
processing arg1
processing arg2
$ ./test.sh --blong 123 -a --clong=456 arg1 arg2
--blong '123' -a --clong '456' -- 'file1' 'file2'
Option b, argument 123
Option a
Option c, argument 456
processing arg1
processing arg2
Donate comment here