第一章:The Missing Code Library--13.调试Shell脚本
2012-12-13 13:04
381 查看
虽然这节并不包含一个真实的脚本,但是在此谈谈开发和调试shell脚本的基础是很有益处的。因为,bug是一定会出现的。我发现的最好的调试策略就是渐增性的生成脚本。一部分脚本程序员对于首次运行即会OK保持着高度乐观的态度,但我发现由小处开始,在一个适宜的规模上的,会很有用处。此外,自由使用echo语句来追踪变量,以及-x选项来显示调试输出,都是很有用的。为了看看它们是怎么做的,我们来调试一个简单的猜数字游戏。
代码:
运行脚本:
调试这个脚本的第一步就是测试下确定生成的数字是完全随机的。我们要获得正在运行的这个脚本所在shell的进程ID,使用$$,然后用%将它减少到一个可以使用的范围内。为了测试这个功能,先直接在命令行上键入:
可以运行,但不怎么随机。为什么?当命令直接在命令行上运行时,PID(进程ID)总是相同的。当在一个脚本中时,命令每次运行都是在一个不同的子shell中,所以PID会变化。下一步是给这个游戏增加基本的逻辑。生成一个1-100的随机数,玩家猜数字,告诉玩家猜的数字是高了还是低了,直到玩家猜出数字。在键入所有的基本的代码后,是时候运行下脚本了。
哈,脚本开发人员的烦恼来了:一个意外的EOF。要搞明白这条信息,回忆下引用起来的信息可能包括换行,所以错误表明在19行并不意味着它就在那儿。它只是说明shell读到那儿,一直错误的匹配引号,除非它能遇到最后一个引号才会停止,此时它意识到一定是哪儿出错了。事实上,19行完全正确:
因此,问题肯定出现在脚本中之前的位置。从shell中得到的错误信息中,唯一有用的就是:它告诉了你哪个字符匹配错误。所以,我会使用grep来尝试提取那些至少有一个引号的行,然后去掉有2个引号的行:
就是它:下引号漏掉了。很容易修复,然后我们接着来:
又一个新问题。由于脚本中的括号表达式很少,所以很容易,肉眼雷达就能发现生成随机数时的少了半个括号。
修正后,再接着运行:
因为100是最大的可能生成的数字了,看来代码中有逻辑错误。这些错误难以察觉,因为没有合适的grep和sed调用来确认错误了。检查代码看看你能不能发现哪儿错了。为了调试这个错误,我准备增加一点echo语句,来输出选定的数字,以及确定我输入的就是正在测试的。有关联的代码是:
事实上,当我修改了echo语句,看到了这2行,我意识到了错误:读入的变量是answer,而测试的变量是guess。一个很傻的错误,但不是一个罕见的错误(特别是如果你有拼写起来很奇怪的变量名的话)。修复这个错误,将answer改为guess即可。
运行结果:
分析脚本:
这个脚本中最严重的潜在bug是没有检查输入。输入除了数字以外的任何东西,脚本都会失败。把第5个脚本--合法化整型输入validint.sh调用下,是最好的。
附上最终正确的脚本:
代码:
#!/bin/sh # hilow -- A simple number-guessing game biggest=100 # maximum number possible guess=0 # guessed by player guesses=0 # number of guesses made number=$(($$ % $biggest) # random number, between 1 and $biggest while [ $guess -ne $number ] ; do echo -n "Guess? " ; read answer if [ "$guess" -lt $number ] ; then echo "... bigger!" elif [ "$guess" -gt $number ] ; then echo "... smaller! fi guesses=$(($guesses + 1) done echo "Right!! Guessed $number in $guesses guesses." exit 0
运行脚本:
调试这个脚本的第一步就是测试下确定生成的数字是完全随机的。我们要获得正在运行的这个脚本所在shell的进程ID,使用$$,然后用%将它减少到一个可以使用的范围内。为了测试这个功能,先直接在命令行上键入:
echo $(($$%100)) 43 echo $(($$%100)) 43 echo $(($$%100)) 43
可以运行,但不怎么随机。为什么?当命令直接在命令行上运行时,PID(进程ID)总是相同的。当在一个脚本中时,命令每次运行都是在一个不同的子shell中,所以PID会变化。下一步是给这个游戏增加基本的逻辑。生成一个1-100的随机数,玩家猜数字,告诉玩家猜的数字是高了还是低了,直到玩家猜出数字。在键入所有的基本的代码后,是时候运行下脚本了。
./hilow.sh ./hilow.sh: line 19: unexpected EOF while looking for matching `"' ./hilow.sh: line 23: syntax error: unexpected end of file
哈,脚本开发人员的烦恼来了:一个意外的EOF。要搞明白这条信息,回忆下引用起来的信息可能包括换行,所以错误表明在19行并不意味着它就在那儿。它只是说明shell读到那儿,一直错误的匹配引号,除非它能遇到最后一个引号才会停止,此时它意识到一定是哪儿出错了。事实上,19行完全正确:
sed -n 19p hilow.sh echo "Right!! Guessed $number in $guesses guesses."
因此,问题肯定出现在脚本中之前的位置。从shell中得到的错误信息中,唯一有用的就是:它告诉了你哪个字符匹配错误。所以,我会使用grep来尝试提取那些至少有一个引号的行,然后去掉有2个引号的行:
grep '"' hilow.sh | egrep -v '.*".*".*' echo "... smaller!
就是它:下引号漏掉了。很容易修复,然后我们接着来:
./hilow.sh ./hilow.sh: line 7: unexpected EOF while looking for matching `)' ./hilow.sh: line 23: syntax error: unexpected end of file
又一个新问题。由于脚本中的括号表达式很少,所以很容易,肉眼雷达就能发现生成随机数时的少了半个括号。
number=$(($$ % $biggest) # random number, between 1 and $biggest
修正后,再接着运行:
./hilow.sh Guess? 33 ... bigger! Guess? 66 ... bigger! Guess? 99 ... bigger! Guess? 100 ... bigger! Guess? Ctrl+C
因为100是最大的可能生成的数字了,看来代码中有逻辑错误。这些错误难以察觉,因为没有合适的grep和sed调用来确认错误了。检查代码看看你能不能发现哪儿错了。为了调试这个错误,我准备增加一点echo语句,来输出选定的数字,以及确定我输入的就是正在测试的。有关联的代码是:
echo -n "Guess? " ; read answer if [ "$guess" -lt $number ] ; then
事实上,当我修改了echo语句,看到了这2行,我意识到了错误:读入的变量是answer,而测试的变量是guess。一个很傻的错误,但不是一个罕见的错误(特别是如果你有拼写起来很奇怪的变量名的话)。修复这个错误,将answer改为guess即可。
运行结果:
./hilow.sh Guess? 50 ... bigger! Guess? 65 ... smaller! Guess? 55 ... smaller! Guess? 52 ... bigger! Guess? 53 Right!! Guessed 53 in 5 guesses.
分析脚本:
这个脚本中最严重的潜在bug是没有检查输入。输入除了数字以外的任何东西,脚本都会失败。把第5个脚本--合法化整型输入validint.sh调用下,是最好的。
附上最终正确的脚本:
#!/bin/sh # hilow -- A simple number-guessing game biggest= # maximum number possible guess= # guessed by player guesses= # number of guesses made number=$(($$ % $biggest)) # random number, between and $biggest while [ $guess -ne $number ] ; do echo -n "Guess? " ; read guess if [ "$guess" -lt $number ] ; then echo "... bigger!" elif [ "$guess" -gt $number ] ; then echo "... smaller!" fi guesses=$(($guesses + )) done echo "Right!! Guessed $number in $guesses guesses." exit
相关文章推荐
- 第一章:The Missing Code Library--7.合法化日期格式
- 第一章:The Missing Code Library--9.一个任意精度浮点计算器
- 第一章:The Missing Code Library--10.锁定文件
- 第一章:The Missing Code Library--8.避免不合要求的echo方法
- 第一章:The Missing Code Library--9.一个任意精度浮点计算器
- 第一章:The Missing Code Library--7.合法化日期格式
- 第一章:The Missing Code Library--6.合法化浮点数输入
- 第一章:The Missing Code Library--6.合法化浮点数输入
- 第一章:The Missing Code Library--5.合法化整型输入
- 第一章:The Missing Code Library--4.优雅的表示大数
- 第一章:The Missing Code Library--4.优雅的表示大数
- 第一章:The Missing Code Library--3.正常化日期格式
- 第一章:The Missing Code Library--11.ANSI颜色序列
- 第一章:The Missing Code Library--2.合法化输入
- 第一章:The Missing Code Library--5.合法化整型输入
- 第一章:The Missing Code Library--8.避免不合要求的echo方法
- 第一章:The Missing Code Library--12.建立一个Shell脚本库
- 第一章:The Missing Code Library--1.在系统路径PATH中寻找程序
- 第一章:The Missing Code Library--2.合法化输入
- 第一章:The Missing Code Library--1.在系统路径PATH中寻找程序