整个文章使用的测试代码如下:
<pre name="code" class="plain">#!/bin/sh
while getopts "abc:" opt
do
case $opt in
"a" ) echo a.$1;;
"b" ) echo b.$1;;
"c" ) echo c.$1,$OPTARG;;
esac
echo optindex:$OPTIND,args:$#
shift
done
对于该脚本,如果使用命令 ./test.sh -a -b -c 123,那么输出应该是什么呢?
一开始,我认为应该是这个样子的:
a.-a
optindex:2,args:4
b.-b
optindex:3,args:3
c.-c,123
optindex:4,args:2
</pre><p><span style="font-size:12px">但是实际上,输出却是如下:</span></p><p></p><pre name="code" class="plain"><pre name="code" class="plain">gls@ubuntu:~/doc/scripts$ ./test.sh -a -b -c 123
a.-a,-b
optindex:2,args:4
c.-b,123,123
optindex:4,args:3
gls@ubuntu:~/doc/scripts$
上面的结果有两个出乎意料的地方:
<pre name="code" class="plain">1、为什么只输出了两次?(case)
2、为什么case中的-b没有进入判断?
其实大家很容易得出什么地方可能会引起这样的问题——shift,但是具体的原因,却有待说明。
因为我对于getopts的具体实现不清楚,因此只能给出自己的猜测,并给出对应的证明。
我的猜测是这样的:1、getopts根据选项在命令行中的偏移来判断该选项位置。在例子中,第一个选项为参数开始位置算起的第一个(简单记为#1),为-a,第二位参数看是位置的第二个(记为#2),为-b,依此类推。那么,一个很重要的信息就是:哪个地方是参数的开始位置呢——毫无疑问,应该是$1。而使用shif恰恰会影响$1的位置(参考shift的作用)。
基于这个猜测,我就能解释上面的两个问题。
很显然,第一次的输出不难理解(第一次输出为第一、二行)。至于第二次输出,则是因为如下原因:使用了shift,导致参数的相对位置发生了变化,$1对应的是-b选项,也就是说此时的-b的位置成了#1(从$1算起的第一个参数)。当getopts处理完第一个选项,准备去取第二个选项时(也就是#2),由于已经发生了偏移,此时的#2为-c,那么就会进入-c(case分支的判断),也就是说直接略过-b的选项。因此,出现了只有两个选项,且-b没有进入判断。
至于验证,也很简单。还是上面的代码。改变参数即可。
<pre name="code" class="plain">gls@ubuntu:~/doc/scripts$ ./test.sh -a -b -c 123 -b -a
a.-a,-b
optindex:2,args:6
c.-b,123,123
optindex:4,args:5
a.-c,123
optindex:5,args:4
gls@ubuntu:~/doc/scripts$
前面两次输出是一样的,只是多了一次输出。可以不难发现,此时的选项有5个!
具体是怎么样的,还得基于刚才的假设,前面的两次与第一次解释一致。至于第三次:
第三次输出是,已经使用了两次shift,$1对应的是-c(第5行的-c可以说明)。也就是说,此时的#1应该对于的是-c。至此,getopts已经解析了2个参数,正要解析第三个参数。而第三个参数,在它看来,应该在#3的位置,而此时#3对应的什么?—-从-c算起的第三个选项,没错,正是-a选项。此时第三次的输出也正是-a应该对应的输出。