第一章:The Missing Code Library--13.调试Shell脚本

   虽然这节并不包含一个真实的脚本,但是在此谈谈开发和调试shell脚本的基础是很有益处的。因为,bug是一定会出现的。我发现的最好的调试策略就是渐增性的生成脚本。一部分脚本程序员对于首次运行即会OK保持着高度乐观的态度,但我发现由小处开始,在一个适宜的规模上的,会很有用处。此外,自由使用echo语句来追踪变量,以及-x选项来显示调试输出,都是很有用的。为了看看它们是怎么做的,我们来调试一个简单的猜数字游戏。

代码:

 1 #!/bin/sh 
 2 # hilow -- A simple number-guessing game 
 3 
 4 biggest=100                           # maximum number possible 
 5 guess=0                               # guessed by player 
 6 guesses=0                             # number of guesses made 
 7 number=$(($$ % $biggest)              # random number, between 1 and $biggest 
 8 
 9 while [ $guess -ne $number ] ; do 
10     echo -n "Guess? " ; read answer 
11     if [ "$guess" -lt $number ] ; then 
12       echo "... bigger!" 
13     elif [ "$guess" -gt $number ] ; then 
14       echo "... smaller!
15     fi 
16     guesses=$(($guesses + 1)
17 done 
18 
19 echo "Right!! Guessed $number in $guesses guesses." 
20 
21 exit 0 

运行脚本:
调试这个脚本的第一步就是测试下确定生成的数字是完全随机的。我们要获得正在运行的这个脚本所在shell的进程ID,使用$$,然后用%将它减少到一个可以使用的范围内。为了测试这个功能,先直接在命令行上键入:

1 echo $(($$%100))
2 43
3 echo $(($$%100))
4 43
5 echo $(($$%100))
6 43

可以运行,但不怎么随机。为什么?当命令直接在命令行上运行时,PID(进程ID)总是相同的。当在一个脚本中时,命令每次运行都是在一个不同的子shell中,所以PID会变化。下一步是给这个游戏增加基本的逻辑。生成一个1-100的随机数,玩家猜数字,告诉玩家猜的数字是高了还是低了,直到玩家猜出数字。在键入所有的基本的代码后,是时候运行下脚本了。

1 ./hilow.sh 
2 ./hilow.sh: line 19: unexpected EOF while looking for matching `"'
3 ./hilow.sh: line 23: syntax error: unexpected end of file

哈,脚本开发人员的烦恼来了:一个意外的EOF。要搞明白这条信息,回忆下引用起来的信息可能包括换行,所以错误表明在19行并不意味着它就在那儿。它只是说明shell读到那儿,一直错误的匹配引号,除非它能遇到最后一个引号才会停止,此时它意识到一定是哪儿出错了。事实上,19行完全正确:

1 sed -n 19p hilow.sh 
2 echo "Right!! Guessed $number in $guesses guesses." 

因此,问题肯定出现在脚本中之前的位置。从shell中得到的错误信息中,唯一有用的就是:它告诉了你哪个字符匹配错误。所以,我会使用grep来尝试提取那些至少有一个引号的行,然后去掉有2个引号的行:

1 grep '"' hilow.sh | egrep -v '.*".*".*'
2           echo "... smaller! 

就是它:下引号漏掉了。很容易修复,然后我们接着来:

1 ./hilow.sh 
2 ./hilow.sh: line 7: unexpected EOF while looking for matching `)'
3 ./hilow.sh: line 23: syntax error: unexpected end of file

又一个新问题。由于脚本中的括号表达式很少,所以很容易,肉眼雷达就能发现生成随机数时的少了半个括号。

1 number=$(($$ % $biggest)              # random number, between 1 and $biggest 

修正后,再接着运行:

 1 ./hilow.sh 
 2 Guess? 33
 3 ... bigger!
 4 Guess? 66
 5 ... bigger!
 6 Guess? 99
 7 ... bigger!
 8 Guess? 100
 9 ... bigger!
10 Guess? Ctrl+C

因为100是最大的可能生成的数字了,看来代码中有逻辑错误。这些错误难以察觉,因为没有合适的grep和sed调用来确认错误了。检查代码看看你能不能发现哪儿错了。为了调试这个错误,我准备增加一点echo语句,来输出选定的数字,以及确定我输入的就是正在测试的。有关联的代码是:

1 echo -n "Guess? " ; read answer 
2 if [ "$guess" -lt $number ] ; then

事实上,当我修改了echo语句,看到了这2行,我意识到了错误:读入的变量是answer,而测试的变量是guess。一个很傻的错误,但不是一个罕见的错误(特别是如果你有拼写起来很奇怪的变量名的话)。修复这个错误,将answer改为guess即可。

运行结果:

 1 ./hilow.sh 
 2 Guess? 50
 3 ... bigger!
 4 Guess? 65
 5 ... smaller!
 6 Guess? 55
 7 ... smaller!
 8 Guess? 52
 9 ... bigger!
10 Guess? 53
11 Right!! Guessed 53 in 5 guesses.

分析脚本:
这个脚本中最严重的潜在bug是没有检查输入。输入除了数字以外的任何东西,脚本都会失败。把第5个脚本--合法化整型输入validint.sh调用下,是最好的。

附上最终正确的脚本:

 1 #!/bin/sh 
 2 # hilow -- A simple number-guessing game 
 3 
 4 biggest=100                           # maximum number possible 
 5 guess=0                               # guessed by player 
 6 guesses=0                             # number of guesses made 
 7 number=$(($$ % $biggest))             # random number, between 1 and $biggest 
 8 
 9 while [ $guess -ne $number ] ; do 
10         echo -n "Guess? " ; read guess
11         if [ "$guess" -lt $number ] ; then 
12           echo "... bigger!" 
13         elif [ "$guess" -gt $number ] ; then 
14           echo "... smaller!" 
15         fi 
16         guesses=$(($guesses + 1))
17 done 
18 
19 echo "Right!! Guessed $number in $guesses guesses." 
20 
21 exit 0 

 

posted @ 2012-12-13 13:04  十舍七匹狼  阅读(1012)  评论(0编辑  收藏  举报