Linux 解析命令行参数

最近需要写脚本封装 helm ,批量进行 k8s 服务的部署。需解析命令行参数场景较复杂。
本文列举各种解析Linux命令行参数三种方式优缺点,及使用限制。

Linux 解析命令行参数有三种方式

直接处理

1
2
3
4
5
6
7
8
9
10
11
#命令本身
$0
#第1、2、3...个参数,依次类推
$1,$2,$3,$4...
#参数的个数,不包括命令本身
$#
#参数数组列表,不包括命令本身
$@
#和$@相同,但"$*"和"$@"(加引号)并不同,
#"$*"将所有的参数解释成一个字符串,而"$@"是一个参数数组
$*

getopts

getoptsbash的内部命令

“getopts ‘:a:b:c:d:ef’”,识别选项字符串

  • "第一个:"消除警告信息。如果命令行中包含了没有在getopts列表中的选项,会有警告信息,在整个getopts字符串前面加上:就可消除警告信息
  • "字母后面:"表示参数,一个冒号就表示这个选项后面必须带有参数,不带:表示不需要参数
  • a|b|c|d|e|f 对应到命令行就是-a ,-b ,-c ,-d, -e ,-f。
  • OPTARG,用来取当前选项的值,另外一个是
  • OPTIND,代表当前选项在参数列表中的位移
  • ?,代表这如果出现了不认识的选项,所进行的操作。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash
while getopts ':a:b:c:d:ef' OPT; do
case $OPT in
a)
echo "$OPT arg is $OPTARG"
;;
b|c|d)
echo "$OPT arg is $OPTARG"
;;
e)
echo "$OPT enabled"
;;
f)
echo "$OPT enabled"
;;
?)
echo "Usage: `basename $0` [options] filename"
esac
done

# 选项参数识别完成之后,如果要取剩余的其它命令行参数,可以使用`shift`把选项参数抹去,位移的长度正好是刚才case循环完毕之后的OPTIND - 1。getopts在处理参数的时候,处理一个开关型选项,OPTIND加1,处理一个带值的选项参数,OPTIND则会加2。
shift $(($OPTIND - 1))

echo $@
1
2
3
4
5
6
7
8
9
# 测试结果
➜ ./getopts.sh -a aaa -b bbb -c ccc -d ddd -e -f hello world
a arg is aaa
b arg is bbb
c arg is ccc
d arg is ddd
e enabled
f enabled
hello world

getopt

getopt是一个外部命令,但通常Linux发行版会自带

注意:mac下的getopt由BSD实现,Linux则是gnu,不兼容(这里踩了很久的坑,为啥 mac 、Linux 总有一边报错-.-)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# mac安装gnu的getopt
brew install gnu-getopt
# 配置环境变量
brew link --force gnu-getopt

# 强行 link 覆盖 mac 本身 getopt 有可能会导致系统不确定风险,可根据操作系统执行不同命令
if [[ `uname` == 'Darwin' ]]; then
echo "Mac OS"
GETOPT=`/usr/local/Cellar/gnu-getopt/2.38.1/bin/getopt`
elif [[ `uname` == 'Linux' ]]; then
echo "Linux"
GETOPT=`getopt`
else
echo 'OS error'
exit 1
fi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 getopt --help

用法:
getopt optstring parameters
getopt [options] [--] optstring parameters
getopt [options] -o|--options optstring [options] [--] parameters

选项:
-a, --alternative 允许长选项以 - 开始
-h, --help 这个简短的用法指南
-l, --longoptions <长选项> 要识别的长选项
-n, --name <程序名> 将错误报告给的程序名
-o, --options <选项字符串> 要识别的短选项
-q, --quiet 禁止 getopt(3) 的错误报告
-Q, --quiet-output 无正常输出
-s, --bash <bash> 设置 bash 引用规则
-T, --test 测试 getopt(1) 版本
-u, --unquoted 不引用输出
-V, --version 输出版本信息

  • -o或–options选项后面是可接受的短选项,如ab:c::,表示可接受的短选项为-a -b -c,
  • -l或–long选项后面是可接受的长选项,用逗号分开,冒号的意义同短选项。
  • :冒号表示参数,一个冒号就表示这个选项后面必须带有参数(没有带参数会报错),但是这个参数可以和选项连在一起写,也可以用空格隔开,比如-a123 和-a 123(中间有空格) 都表示123是-a的参数;
  • 不带:表示不需要参数
  • ::表示这个选项的参数是可选的,即可以有参数,也可以没有参数,但要注意有参数时,参数与选项之间不能有空格(有空格会报错),这一点和一个冒号时是有区别的。
  • 如果短选项带参数且参数可选时,参数必须紧贴选项,例如-carg而不能是-c arg
  • 如果长选项带参数且参数可选时,参数和选项之间用“=”,例如–clong=arg而不能是–clong arg

示例

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
50
51
52
#!/bin/bash

echo "传入参数:$@"

# 需以getopt与传入参数--分隔
ARGS=`getopt -o ab:c:: --long along,blong:,clong:: -n "$0" -- "$@"`
if [ $? -ne 0 ]; then
echo "getopt error ..."
exit 1
fi
# 重新排列参数顺序
set -- ${ARGS}

echo "排列后参数:$@"

# 通过shift和while循环处理参数
while true
do
case "$1" in
-a|--along)
echo "$1 is enabled";
shift
;;
-b|--blong)
echo "$1 arg is $2";
shift 2
;;
-c|--clong)
case "$2" in
"")
echo "$1 is enabled";
shift 2
;;
*)
echo "$1 arg is $2";
shift 2;
;;
esac
;;
--)
shift
break
;;
*)
echo "Internal error!"
exit 1
;;
esac
done

#处理剩余的参数
echo "剩余参数:$@"
1
2
3
4
5
6
7
8
9
10
11
12
13
# 测试结果
➜ ./getopt.sh -a -b bbb -c -cccc hello world --along --blong bbb --clong=ccc --clong
传入参数:-a -b bbb -c -cccc hello world --along --blong bbb --clong=ccc --clong
排列后参数:-a -b 'bbb' -c '' -c 'ccc' --along --blong 'bbb' --clong 'ccc' --clong '' -- 'hello' 'world'
-a is enabled
-b arg is 'bbb'
-c arg is ''
-c arg is 'ccc'
--along is enabled
--blong arg is 'bbb'
--clong arg is 'ccc'
--clong arg is ''
剩余参数:'hello' 'world'

getopt getopts 区别

getopt getopts
支持短选项
支持长选项
支持可选参数
参数重排序
case -a|–along) a)
参数顺序 任意顺序 所有选项参数必须写在其它参数的前面
每处理完一个位置参数后都需要自己shift来跳到下一个位置 在最后使用 shift $(($OPTIND - 1))来跳到parameters的位置
外部命令,通常Linux发行版会自带 bash内部命令

总结

  • 简单脚本可以直接处理
  • 大多数情况使用getopts
  • 处理复杂场景使用getopt

Linux 解析命令行参数
https://zhengshuoo.github.io/posts/014-linux-command-params
作者
zhengshuo
发布于
2023年3月10日
许可协议