本文详细介绍了shell 脚本调试技术,包括使用echo, tee , trap 等命令输出关键信息,跟踪变量的值,在脚本中植入调试钩子,使用“-n”选项进行shell脚本的语法检查, 使用“-x”选项实现shell脚本逐条语句的跟踪,巧妙地利用shell的内置变量增强“-x”选项的输出信息等。
一. 前言 现在系统地学习一些重要的shell脚本调试技术,相信对很多shell的初学者有很大帮助的。 阅读下面内容前,要求读者具有基本的shell编程知识。本文实验平台是Bash3.1+Redhat Enterprise Server 4.0,但所介绍的调试技巧应该也同样适用于其它shell和Unix平台。
二. 在shell脚本中输出调试信息
1. 使用trap命令
表 1. shell伪信号
通过捕获EXIT信号,我们可以在shell脚本中止执行或从函数中退出时,输出某些想要跟踪的变量的值,并由此来判断脚本的执行状态以及出错原因,其使用方法是:
通过捕获ERR信号,我们可以方便的追踪执行不成功的命令或函数,并输出相关的调试信息,以下是一个捕获ERR信号的示例程序,其中的$LINENO是一个shell的内置变量,代表shell脚本的当前行号。
复制代码代码示例:
$ cat -n exp1.sh
ERRTRAP() { echo "[LINE:$1] Error: Command or function exited with status $?" } foo() { return 1; } trap 'ERRTRAP $LINENO' ERR abc foo
输出结果: 在调试过程中,为了跟踪某些变量的值,我们常常需要在shell脚本的许多地方插入相同的echo语句来打印相关变量的值,这种做法显得烦琐而笨拙。而通过捕获DEBUG信号,我们只需要一条trap语句就可以完成对相关变量的全程跟踪。
下面是一个通过捕获DEBUG信号来跟踪变量的示例程序:
复制代码代码示例:
$ cat –n exp2.sh
#!/bin/bash trap 'echo “before execute line:$LINENO, a=$a,b=$b,c=$c”' DEBUG a=1 if [ "$a" -eq 1 ] then b=2 else b=1 fi c=3 echo "end"
输出结果: 从运行结果中我们可以清晰的看到每执行一条命令之后,相关变量的值的变化。同时,从运行结果中打印出来的行号来分析,可以看到整个脚本的执行轨迹,能够判断出哪些条件分支执行了,哪些条件分支没有执行。
2. 使用tee命令
tee命令会从标准输入读取数据,将其内容输出到标准输出设备,同时又可将内容保存成文件。例如有如下的脚本片段,其作用是获取本机的ip地址:
复制代码代码示例:
ipaddr=`/sbin/ifconfig | grep 'inet addr:' | grep -v '127.0.0.1'
| cut -d : -f3 | awk '{print $1}'` #注意=号后面的整句是用反引号(数字1键的左边那个键)括起来的。 echo $ipaddr
运行这个脚本,实际输出的却不是本机的ip地址,而是广播地址,这时我们可以借助tee命令,输出某些中间结果,将上述脚本片段修改为:
复制代码代码示例:
ipaddr=`/sbin/ifconfig | grep 'inet addr:' | grep -v '127.0.0.1'
| tee temp.txt | cut -d : -f3 | awk '{print $1}'` echo $ipaddr
之后,将这段脚本再执行一遍,然后查看temp.txt文件的内容:
复制代码代码示例:
$ cat temp.txt
inet addr:192.168.0.1 Bcast:192.168.0.255 Mask:255.255.255.0 可以发现中间结果的第二列(列之间以:号分隔)才包含了IP地址,而在上面的脚本中使用cut命令截取了第三列,所以我们只需将脚本中的cut -d : -f3改为cut -d : -f2即可得到正确的结果。 分析以上的脚本例子,我们也许并不需要tee命令的帮助,比如我们可以分段执行由管道连接起来的各条命令并查看各命令的输出结果来诊断错误,但在一些复 杂的shell脚本中,这些由管道连接起来的命令可能又依赖于脚本中定义的一些其它变量,这时我们想要在提示符下来分段运行各条命令就会非常麻烦了,简单 地在管道之间插入一条tee命令来查看中间结果会更方便一些。
3. 使用"调试钩子"
复制代码代码示例:
if [ “$DEBUG” = “true” ]; then
echo “debugging” #此处可以输出调试信息 fi 这样的代码块通常称之为“调试钩子”或“调试块”。在调试钩子内部可以输出任何您想输出的调试信息,使用调试钩子的好处是它是可以通过DEBUG变量来控 制的,在脚本的开发调试阶段,可以先执行export DEBUG=true命令打开调试钩子,使其输出调试信息,而在把脚本交付使用时,也无需再费事把脚本中的调试语句一一删除。
如果在每一处需要输出调试信息的地方均使用if语句来判断DEBUG变量的值,还是显得比较 繁琐,通过定义一个DEBUG函数可以使植入调试钩子的过程更简洁方便,如下面代码所示:
复制代码代码示例:
$ cat –n exp3.sh
DEBUG() { if [ "$DEBUG" = "true" ]; then $@ fi } a=1 DEBUG echo "a=$a" if [ "$a" -eq 1 ] then b=2 else b=1 fi DEBUG echo "b=$b" c=3 DEBUG echo "c=$c" 以上DEBUG函数中,会执行任何传给它的命令,并且这个执行过程是可以通过DEBUG变量的值来控制的,可以把所有跟调试有关的命令都作为DEBUG函数的参数来调用。 (责任编辑:IT) |