赞
踩
getopt --help
用法:
getopt optstring parameters
getopt [options] [--] optstring parameters
getopt [options] -o|--options optstring [options] [--] parameters
解析命令选项。
选项:
-a, --alternative 允许长选项以单个 - 开头
-l, --longoptions 要识别的长选项
-n, --name 要报告其错误的程序名称
-o, --options 要识别的短选项名称
-q, --quiet 禁止 getopt(3) 的错误报告
-Q, --quiet-output 无常规输出
-s, --shell 设置 的引用习惯
-T, --test getopt(1) 版本测试
-u, --unquoted 不引用输出
-h, --help display this help
-V, --version display version
可以看到getopt有3种语法用法。
getopt optstring parameters
getopt [options] [--] optstring parameters
getopt [options] -o|--options optstring [options] [--] parameters
其中,optstring可以理解为“解析规则”,parameters可以理解为“待解析的字符串”,getopt的作用就是,依据“解析规则”对“待解析的字符串”进行解析,将散乱、自由的命令行选项和参数进行解析,得到一个完整的、规范化的参数列表,这样再使用while、case和shift进行处理即可。
本章节(1.2 命令行选项tips)选自(有删减):设计shell脚本选项:getopt
在学习getopt如何使用之前,必须先知道命令行的一些常识。这些,都可以通过getopt来实现,但有些实现起来可能会比较复杂。
parameter和argument都表示参数,前者通常表示独立性的参数,后者通常表示依赖于其它实体的参数。parameter的含义更广,argument可以看作parameter的一种。
例如,定义函数时function foo(x,y){CODE}
,函数的参数x和y称为parameter。调用函数并传递参数时,foo(arg1,arg2)
中的arg1和arg2都是依赖于函数的,称为argument更合适,当然也可以称为更广泛的parameter。
再例如,一个命令行:
tar -zcf a.tar.gz /etc/pki
粗分的话,-z
、-c
、-f
、a.tar.gz
、/etc/pki
都可以称为parameter。细分的话:
本文要介绍的是getopt,所以只考虑命令行参数的情况。
Linux中绝大多数命令都提供了短选项和长选项。一般来说,短选项是只使用一个"-“开头,选项部分只使用一个字符,长选项是使用两个短横线(即”–")开头的。
例如"-a
"是短选项,"--append
"是长选项。
一般来说,选项的顺序是无所谓的,但并非绝对如此,有时候某些选项必须放在前面,必须放在某些选项的前面、后面。
一般来说,短选项:
可以通过一个短横线"-"将多个短选项连接在一起,但如果连在一起的短选项有参数的话,则必须作为串联的最后一个字符。
例如"-avz"其实会被解析为"-a -v -z",tar -zcf a.tar.gz
串联了多个短选项,但"-f"选项有参数a.tar.gz,所以它必须作为串联选项的最后一个字符(实测-f可不在最后,可能是命令做了优化)。
短选项的参数可以和选项名称连在一起,也可以是用空白分隔。例如-n 3
和-n3
是等价的,数值3都是"-n"选项的参数值。
如果某个短选项的参数是可选的,那么它的参数必须紧跟在选项名后面,不能使用空格分开。
一般来说,长选项:
可以使用等号或空白连接两种方式提供选项参数。例如--file=FILE
或--file FILE
。
如果某个长选项的参数是可选的,那么它的参数必须使用"="连接。
长选项一般可以缩写,只要不产生歧义即可。
例如,ls命令,以"a"开头的长选项有3个。
$ ls --help | grep -- '--a'
-a, --all do not ignore entries starting with .
-A, --almost-all do not list implied . and ..
--author with -l, print the author of each file
如果想要指定--almost-all
,可以缩写为--alm
;如果想要指定--author
,可以缩写为--au
。如果只缩写为--a
,bash将给出错误提示,长选项出现歧义:
$ ls --a
ls: option '--a' is ambiguous; possibilities: '--all' '--author' '--almost-all'
Try 'ls --help' for more information.
有不同类型的命令行选项,这些选项可能不需要参数,也可能参数是可选的,也可能是强制要求参数的。
前面说了,如果某个选项的参数是可选的,那么它的参数必须不能使用空格将参数和选项分开。如果使用空格分隔,则无法判断它的下一个元素是该选项的参数还是非选项类型的参数。
例如,-c
和--config
选项的参数是可选的,要向这两个选项提供参数,必须写成-cFILE
、--config=FILE
,如果写成-c FILE
、--config FILE
,那么命令行将无法判断这个FILE是提供给选项的参数,还是非选项类型的参数。
一般来说,使用可选参数的情况非常少,至少我目前回忆不起来这样的命令(mysql -p选项是一个)。
--
将选项(及它们的选项参数)与非选项类型参数进行分隔unix的命令行中,总是可以在非选项类型的参数之前加上--
,表示选项和选项参数到此为止,后面的都是非选项类型的参数。
例如:
$ cat hello.txt nihao shijie this is a line contains hello this is a other sentence. hello2 --hello end, thanks. $ grep -n hello hello.txt 2:this is a line contains hello 4:hello2 5:--hello #当想检索hello.txt里的--hello字符串时,--hello会被解析为grep命令的选项而报错 #grep -n "--hello" hello.txt 加上双引号也报错 $ grep -n --hello hello.txt grep: unrecognized option '--hello' 用法: grep [选项]... PATTERN [FILE]... 试用'grep --help' 来获得更多信息。 #一个可行的方法是,加引号并转义 $ grep -n "\-\-hello" hello.txt 5:--hello #一个更优雅的方式是使用--将[选项]和 PATTERN [FILE]分隔开 #grep [选项]... PATTERN [FILE]... $ grep -n -- --hello hello.txt 5:--hello
上述例程就是--
的作用,可以强制禁止一个命令把--
后边的内容识别为选项(或选项参数)。当然,也有例外,并不是所有的命令,在设计的时候都支持了--
的这一功能,如:
#比如想要使用echo回显字符串"-n"到屏幕
#结果发现什么都没显示,是因为-n是echo的一个选项
$ echo "-n"
$
#那加上--,禁止echo把-n识别为选项
#结果显示,连同--都被打印出来了,即echo命令在实现的时候,并没有将--作为分割选项和非选项的标志。
$ echo -- "-n"
-- -n
例如seq命令:
seq -w -5 -1 5
其中-5和-1都是负数非选项类型的参数。
很多unix命令都将选项进行模块化设计。例如ip命令,address模式、route模式、link模式等等。
ip addr OPTIONS
ip route OPTIONS
ip link OPTIONS
ip neigh OPTIONS
有些命令还有比较个性化的选项。
比如head命令,-n NUM
选项,即可以指定为-3
,也可以指定为-n 3
或-n3
。
再比如有的命令支持逻辑运算,例如find命令的-a
、-o
选项。
getopt optstring parameters
例:
$ getopt ab:cd -a -b arg_b -d pram1 pram2 pram3
-a -b arg_b -d -- pram1 pram2 pram3
$
主要理解 ab:cd
的意义。这里定义的都是短选项,4个字母代表有4个可以解析出来的选项;b后面的冒号表示这个选项需要一个参数,如果不给选项b一个参数,就会报错:
$ getopt ab:cd -a -d pram1 pram2 pram3 -b
getopt: 选项需要一个参数 -- b
-a -d -- pram1 pram2 pram3
如果上述命令改为下图所示,则不会报错,原因是-b
后的-d
作为了-b
这个选项的参数。这就需要后续再shell脚本的-b
分支中,对齐参数的合法性进行简单地判断。
$ getopt ab:cd -a -b -d pram1 pram2 pram3
-a -b -d -- pram1 pram2 pram3
在1.2.4 使用--
将选项(及它们的选项参数)与非选项类型参数进行分隔中,介绍了--
分隔选项的机制,也详细讲述了并不是所有的命令(如echo)都支持使用--
来分隔选项和非选项。如果我们实现一个脚本(一个命令)的时候,如果使用getopt对参数进行处理,那么我们的脚本是支持--
分隔选项和非选项的。
$ getopt ab:cd -ad value1 -- -b best1 value2 value3
-a -d -- value1 -b best1 value2 value3
用法一和用法二、三在功能上也是有区别的。用法一输出的参数都是不带引号的。而另外两种格式输出的参数都是带引号的。重要的区别不在引号上,而是这种用法不支持处理带空格和引号的参数值。它会将空格当作参数分隔符,而不是根据双引号将二者当作一个参数。
加入有这样一个脚本:
test.sh
#!/bin/sh while true do case "$1" in -a) append="$2" shift 2 ;; --) shift break ;; *) break ;; esac done for param in "$@" do echo ${param}${append} done
它实现了对输入的参数进行回显的功能,并且当指定-a “string” 时,这个脚本会在将要回显的每个数据后追加上"string"。如:
$ ./test.sh nihao shijie hello world "this is a sentence"
nihao
shijie
hello
world
this is a sentence
$ ./test.sh -a xxxxxxxxxxx nihao shijie hello world "this is a sentence"
nihaoxxxxxxxxxxx
shijiexxxxxxxxxxx
helloxxxxxxxxxxx
worldxxxxxxxxxxx
this is a sentencexxxxxxxxxxx
不过这个脚本有个弊端,它没有参数进行规范化处理(getopt就是用来规范化参数的),导致-a放在命令最后会失去原本想要达到的效果:
$ ./test.sh nihao shijie hello world "this is a sentence" -a xxxxxxxxxxx
nihao
shijie
hello
world
this is a sentence
-a
xxxxxxxxxxx
那我们加上getopt的用法一
test.sh
# 将一下代码加到test.sh的while true的前边
# getopt的用法一
ARGS=$(getopt a: "$@")
#set -- $ARGS的作用是用$ARGS里的内容替换当前shell的$1 $2 ...的内容
set -- $ARGS
此时测试-a放在命令最后的效果,可以看到即使放到了最后,-a同样达到了预期的效果:
$ ./test.sh nihao shijie hello world -a xxxxxxxxxxx
nihaoxxxxxxxxxxx
shijiexxxxxxxxxxx
helloxxxxxxxxxxx
worldxxxxxxxxxxx
但是此处使用的是getopt的用法一,不能正确处理带空格和引号的字符串,我们期望的打印是"this is a sentencexxxxxxxxxxx ",而不是把一句话拆成4个单词:
$ ./test.sh nihao shijie hello world -a xxxxxxxxxxx "this is a sentence"
nihaoxxxxxxxxxxx
shijiexxxxxxxxxxx
helloxxxxxxxxxxx
worldxxxxxxxxxxx
thisxxxxxxxxxxx
isxxxxxxxxxxx
axxxxxxxxxxx
sentencexxxxxxxxxxx
此时就需要用到用法二或用法三
getopt [options] [--] optstring parameters
将上边的test.sh的代码,改为使用getopt的用法二对参数进行处理。同时增加一条echo语句打印getopt处理完之后的参数。
# getopt的用法二
ARGS=$(getopt -- a: "$@")
echo $ARGS
#set -- $ARGS的作用是用$ARGS里的内容替换当前shell的$1 $2 ...的内容
set -- $ARGS
测试是否可以正常解析带引号和空格的字符串:
$ ./test.sh nihao shijie hello world -a xxxxxxxxxxx "this is a sentence"
-a 'xxxxxxxxxxx' -- 'nihao' 'shijie' 'hello' 'world' 'this is a sentence'
'nihao''xxxxxxxxxxx'
'shijie''xxxxxxxxxxx'
'hello''xxxxxxxxxxx'
'world''xxxxxxxxxxx'
'this'xxxxxxxxxxx'
is'xxxxxxxxxxx'
a'xxxxxxxxxxx'
sentence''xxxxxxxxxxx'
采用用法二之后,发现并没有解决方法一的短板,且多了好多引号,显得更乱了。从新增的echo $ARGS
打印来看,方法二解析完的参数,是带引号的,并且正确的使用引号圈出了"this is a sentence"。正是这些引号,导致echo的时候引号增多。这是就需要用到eval命令。在set – $ARGS前加上eval
:
# getopt的用法二
ARGS=$(getopt -- a: "$@")
echo $ARGS
#set -- $ARGS的作用是用$ARGS里的内容替换当前shell的$1 $2 ...的内容
eval set -- $ARGS
再次测试:
$ ./test.sh nihao shijie hello world -a xxxxxxxxxxx "this is a sentence"
-a 'xxxxxxxxxxx' -- 'nihao' 'shijie' 'hello' 'world' 'this is a sentence'
nihaoxxxxxxxxxxx
shijiexxxxxxxxxxx
helloxxxxxxxxxxx
worldxxxxxxxxxxx
this is a sentencexxxxxxxxxxx
成功符合预期地执行。
关于eval
的介绍,可以参考man手册的eval的example
getopt [options] -o|--options optstring [options] [--] parameters
用法三和用法二差不多,只是多了一个-o
选项,一般指定长选项时(-l
选项),会配合使用-o
(其他的情况我也不是很了解)。-n
选项指定引用的错误程序名。
ARGV=$(getopt -n $(basename $0) -o hd:s: -l help,destination:,source: -- "$@")
eval set -- "$ARGV"
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。