基本方法

表面上看,命令行选项也没什么特殊的。在命令行上,它们紧跟在脚本名之后,就跟命令行 参数一样。实际上,如果愿意,你可以像处理命令行参数一样处理命令行选项。

在提取每个单独参数时,用case语句(参见第12章)来判断某个参数是否为选项。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
$ cat test15.sh
#!/bin/bash
# extracting command line options as parameters #
echo
while [ -n "$1" ]
do
   case "$1" in
     -a) echo "Found the -a option" ;;
     -b) echo "Found the -b option" ;;
     -c) echo "Found the -c option" ;;
      *) echo "$1 is not an option" ;;
   esac
shift done
$
$ ./test15.sh -a -b -c -d
Found the -a option
Found the -b option
Found the -c option
-d is not an option
$

case语句会检查每个参数是不是有效选项。如果是的话,就运行对应case语句中的命令。 不管选项按什么顺序出现在命令行上,这种方法都适用。

使用getopt命令

基本用法

getopt命令是一个在处理命令行选项和参数时非常方便的工具。它能够识别命令行参数, 从而在脚本中解析它们时更方便。

getopt命令可以接受一系列任意形式的命令行选项和参数,并自动将它们转换成适当的格式。

它的命令格式如下:

getopt optstring parameters 

optstring是这个过程的关键所在。它定义了命令行有效的选项字母,还定义了哪些选项字母需要参数值。

首先,在optstring中列出你要在脚本中用到的每个命令行选项字母。然后,在每个需要参数值的选项字母后加一个冒号。

getopt命令会基于你定义的optstring解析提供的参数。

下面是个getopt如何工作的简单例子。

$ 
getopt ab:cd -a -b test1 -cd test2 test3 
-a -b test1 -c -d -- test2 test3
$

optstring定义了四个有效选项字母:a、b、c和d。冒号(:)被放在了字母b后面,因为b选项需要一个参数值。

当getopt命令运行时,它会检查提供的参数列表(-a -b test1 -cd test2 test3),并基于提供的optstring进行解析。注意,它会自动将-cd选项分成两个单独的选项,并插入双破折线来分隔行中的额外参数。

如果指定了一个不在optstring中的选项,默认情况下,getopt命令会产生一条错误消息。如果想忽略这条错误消息,可以在命令后加-q选项。

getopt -q ab:cd -a -b test1 -cde test2 test3

在脚本中的用法

可以在脚本中使用getopt来格式化脚本所携带的任何命令行选项或参数,但用起来略微复杂。

方法是用getopt命令生成的格式化后的版本来替换已有的命令行选项和参数。用set命令能够做到。

set命令的选项之一是双破折线(–),它会将命令行参数替换成set命令的命令行值。

然后,该方法会将原始脚本的命令行参数传给getopt命令,之后再将getopt命令的输出传给set命令,用getopt格式化后的命令行参数来替换原始的命令行参数,看起来如下所示。

set -- $(getopt -q ab:cd "$@")

现在原始的命令行参数变量的值会被getopt命令的输出替换,而getopt已经为我们格式化好了命令行参数。

./test18.sh -ac

getopt命令并不擅长处理带空格和引号的参数值。它会将空格当作参数分隔符,而不是根 据双引号将二者当作一个参数。

使用更高级的getopts

与getopt不同,前者将命令行上选项和参数处理后只生成一个输出,而getopts命令能够 和已有的shell参数变量配合默契。

每次调用它时,它一次只处理命令行上检测到的一个参数。处理完所有的参数后,它会退出并返回一个大于0的退出状态码。这让它非常适合用解析命令行所有参数的循环中。

getopts命令的格式如下:

getopts optstring variable

optstring值类似于getopt命令中的那个。有效的选项字母都会列在optstring中,如果 选项字母要求有个参数值,就加一个冒号。要去掉错误消息的话,可以在optstring之前加一个 冒号。getopts命令将当前参数保存在命令行中定义的variable中。

getopts命令会用到两个环境变量。如果选项需要跟一个参数值,OPTARG环境变量就会保 存这个值。OPTIND环境变量保存了参数列表中getopts正在处理的参数位置。这样你就能在处 理完选项之后继续处理其他命令行参数了。

让我们看个使用getopts命令的简单例子。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
$ cat test19.sh
#!/bin/bash
# simple demonstration of the getopts command #
echo
while getopts 🆎c opt
do
   case "$opt" in
      a) echo "Found the -a option" ;;
      b) echo "Found the -b option, with value $OPTARG";;
      c) echo "Found the -c option" ;;
      *) echo "Unknown option: $opt";;
esac done
$
$ ./test19.sh -ab test1 -c
Found the -a option
Found the -b option, with value test1
Found the -c option
$

while语句定义了getopts命令,指明了要查找哪些命令行选项,以及每次迭代中存储它们 的变量名(opt)。

你会注意到在本例中case语句的用法有些不同。getopts命令解析命令行选项时会移除开头的单破折线,所以在case定义中不用单破折线。

getopts命令有几个好用的功能。对新手来说,可以在参数值中包含空格。

$ ./test19.sh -b "test1 test2" -a

另一个好用的功能是将选项字母和参数值放在一起使用,而不用加空格。

$ ./test19.sh -abtest1
Found the -a option
Found the -b option, with value test1
$

getopts命令能够从-b选项中正确解析出test1值。除此之外,getopts还能够将命令行上 找到的所有未定义的选项统一输出成问号。

$ ./test19.sh -d
Unknown option: ?
$

optstring中未定义的选项字母会以问号形式发送给代码。

getopts命令知道何时停止处理选项,并将参数留给你处理。在getopts处理每个选项时,它会将OPTIND环境变量值增一。在getopts完成处理时,你可以使用shift命令和OPTIND值来 移动参数。

$ cat test20.sh
#!/bin/bash
# Processing options & parameters with 	getopts #
echo
while getopts 🆎cd opt	
do
   case "$opt" in
   a) echo "Found the -a option"  ;;
   b) echo "Found the -b option, with value $OPTARG" ;;
   c) echo "Found the -c option"  ;;
   d) echo "Found the -d option"  ;;
   *) echo "Unknown option: $opt" ;;
   esac
done
#
shift $[ $OPTIND - 1 ]
#
echo
count=1
for param in "$@"
do
   echo "Parameter $count: $param"
   count=$[ $count + 1 ]
done
#
$
$ ./test20.sh -a -b test1 -d test2 test3 test4
Found the -a option
Found the -b option, with value test1
Found the -d option
Parameter 1: test2
Parameter 2: test3
Parameter 3: test4
$

将选项标准化

在创建shell脚本时,显然可以控制具体怎么做。你完全可以决定用哪些字母选项以及它们的 用法。

但有些字母选项在Linux世界里已经拥有了某种程度的标准含义。如果你能在shell脚本中支 6 持这些选项,脚本看起来能更友好一些。