解析命令行的选项和参数
$1 $2 ...
被称作位置参数(posotional parameters)。
1. shift⚓
shift [n]
把从 n+1 ... 的位置参数重命名为 $1 ...
The positional parameters from n+1 ... are renamed to $1 ....
n 默认是 1
该指令是用于修改位置参数的指向,比如执行一次shift 2
就相当于把向后数2个的那个参数变成被$1
引用:
这是一个普通的命令:
$0 $1 $2 $3
| | | |
v v v v
ls -l /root /etc
执行 shift 2 之后就变成了
$0 $1
| |
v v
ls -l /root /etc
2. getopts⚓
这是 bash 内置的命令,用于解析输入的选项与参数,主要用于处理小的脚本。
getopts optstring name [args]
-
optstring:定义了要解析的选项及有无参数,只能解析短选项。带有参数的选项后面要有一个冒号,在输入选项和参数时要使用空格隔开
-
name:则是一个自定义变量,每次调用时都会把下一个选项放进该变量,并把下一个参数的索引放到环境变量
OPTIND
中,若选项有要求参数,会把参数放到OPTARG
环境变量中。 -
args:getopts 通常分析位置参数,但如果 args 中提供了参数,getopts 会分析这些参数,而不再去管命令行传入的参数了。意思就是可以在这里写 optstring 中定义的选项直接传递给 getopts 去解析。
getopts 测试脚本getopts-test.sh
:
#!/bin/sh
# 测试 getops 的使用
echo "original parameters: ($@)"
while getopts 'abc:' opt
do
echo "process option $opt"
case $opt in
a)
echo "get a, OPRIND=$OPTIND, OPTARG=$OPTARG"
;;
b)
echo "get b, OPRIND=$OPTIND, OPTARG=$OPTARG"
;;
c)
echo "get c, OPRIND=$OPTIND, OPTARG=$OPTARG"
;;
*)
echo "error option $opt, , OPRIND=$OPTIND, OPTARG=$OPTARG"
exit 1
esac
done
echo "now OPTIND=$OPTIND"
shift $(expr $OPTIND - 1)
echo "remain parameters: ($@)"
执行结果:
# 正常执行
[sink@dev ~]$ ./getopts-test.sh -a -b -c 23 opq
original parameters: (-a -b -c 23 opq)
process option a
get a, OPRIND=2, OPTARG=
process option b
get b, OPRIND=3, OPTARG=
process option c
get c, OPRIND=5, OPTARG=23
now OPTIND=5
remain parameters: (opq)
# 缺少参数
[sink@dev ~]$ ./getopts-test.sh -c
original parameters: (-c)
./getopts-test.sh: option requires an argument -- c
process option ?
error option ?, , OPRIND=2, OPTARG=
# 未知选项
[sink@dev ~]$ ./getopts-test.sh -d
original parameters: (-d)
./getopts-test.sh: illegal option -- d
process option ?
error option ?, , OPRIND=2, OPTARG=
带有args的getopts-test.sh
版本:
……
while getopts 'abc:' opt -b
……
执行结果:
[sink@dev ~]$ ./getopts-test.sh -a -c 2 dsf
original parameters: (-a -c 2 dsf)
process option b
get b, OPRIND=2, OPTARG=
now OPTIND=2
remain parameters: (-c 2 dsf)
# 不论传入的参数为何,都不再解析这些参数
[sink@dev ~]$ ./getopts-test.sh -d dsf
original parameters: (-d dsf)
process option b
get b, OPRIND=2, OPTARG=
now OPTIND=2
remain parameters: (dsf)
3. getopt⚓
getopt optstring parameters
getopt [options] [--] optstring parameters
getopt [options] -o|--options optstring [options] [--] parameters
OPTIONS
-a, --alternative 允许长选项使用单个 '-' 开头.
-l, --longoptions longopts longopts 的格式为"abcd,efg:,hijk::",后跟一个冒号表示需要参数,
后跟两个冒号表示参数可选
-o, --options shortopts shortopts 的格式为"ab:c::",后跟一个冒号表示需要参数,
后跟两个冒号表示参数可选
-n, --name progname 报告错误时所用的程序名
短选项:
- 若该选项需要参数,那么既可以直接跟在选项后面,也可以使用空格分隔;
- 若该选项的参数可选,那么参数就必须直接跟在选项后面;
- 还可以在单个
-
后面直接写好几个不能传参的选项。
长选项:
- 若该选项需要参数,那么既可以使用
=
分隔,也可以使用空格分隔; - 若该选项的参数可选,那么参数就必须使用
=
分隔; - 长选项还可以缩写,只要缩写是明确的。
命令的输出:
- 如果可选参数的选项没有传参,那么会其参数变成空字符串
""
; - 返回的字符串会按照
选项 参数 -- 非选项参数
的格式。非选项带有的参数会在--
后面出现; - 好像不能正确解析带有空格的字符串,会将字符串进行拆分。
getopt-test.sh
:
#!/bin/sh
# 测试 getopt 命令
args=$(getopt -n $0 -l "int,char:,string::" -o "ab:c::" -- $@)
if [ $? != 0 ]
then
exit 1
fi
echo "args=($args)"
# 重新设定位置参数
set -- $args
echo "\$#=$#, \$@=($@)"
while true
do
case $1 in
--int|-a)
echo "get $1"
shift
;;
--char|-b)
echo "get $1, value=$2"
shift 2
;;
--string|-c)
echo "get $1, value=\"$2\""
shift 2
;;
--)
shift
break
;;
*)
echo "It's impossible to get here."
exit 1
esac
done
echo "\$#=$#, remain args=($@)"
正常使用看看:
[sink@dev ~]$ sh getopt-test.sh -a -c'a f' --s abcd --int 'hi jk'
args=( -a -c 'a' --string '' --int -- 'f' 'abcd' 'hi' 'jk')
$#=11, $@=(-a -c 'a' --string '' --int -- 'f' 'abcd' 'hi' 'jk')
get -a
get -c, value="'a'"
get --string, value="''"
get --int
$#=4, remain args=('f' 'abcd' 'hi' 'jk')
若可选参数的选项使用了错误的使用方法,那么其参数会变成非选项参数:
[sink@dev ~]$ sh getopt-test.sh -a -c dsaf --s abcd --int 'hi jk'
args=( -a -c '' --string '' --int -- 'dsaf' 'abcd' 'hi' 'jk')
$#=11, $@=(-a -c '' --string '' --int -- 'dsaf' 'abcd' 'hi' 'jk')
get -a
get -c, value="''"
get --string, value="''"
get --int
$#=4, remain args=('dsaf' 'abcd' 'hi' 'jk')
4. 总结⚓
getopts 与 getopt 的不同之处在于:
- getopts
- bash 内置命令
- 只能处理短选项
- 没有可选参数的选项设定
- 可以直接读取命令行的选项和参数进行处理
- getopt
- 一般Linux发行版都会内置的工具
- 能够同时处理短选项和长选项,并且长选项可缩写
- 并不是所有的Linux发行版都实现了可选参数的设定
- 需要先读取命令行的选项和参数,返回一个字符串,然后进行二次设定位置参数
- 不能正确处理带有空格的参数