1 Shell脚本调试概述
Shell脚本调试就是发现引发脚本错误的原因以及在脚本源代码中定位发生错误的行,常用的手段包括分析输出的错误信息,通过在脚本中加入调试语句,输出调试信息来辅助诊断错误,利用调试工具等
Shell解释器缺乏相应的调试机制和调试工具的支持,其输出的错误信息又往往很不明确,因此,Shell脚本调试是一个令程序员头痛的问题
2 Shell脚本的错误可分为两类:
语法错误(syntaxerror),脚本无法执行到底
misskey.sh脚本演示漏写关键字错误
#!/bin/bash var=0 while : if [$var -gt 3] then break fi let "var=var+1" done
缺少关键字do
Shell脚本能够执行完毕,但是并不是按照我们所期望的方式运行,即存在逻辑错误
脚本演示逻辑错误
#!/bin/bash count=1 #用于记录进入while循环的次数 MAX=5 while [ "$SECONDS" -le"$MAX" ] do echo "This is the $count time to sleep." count=$count+1 sleep 2 done echo "The running time of this scriptis $SECONDS"
3 调试技术
(1)使用trap命令
trap是Linux的内建命令,它用于捕捉信号,trap命令可以指定收到某种信号时所执行的命令
trap command sig1 sig2 …sigN
Shell脚本在执行时,会产生三个所谓的“伪信号”,(之所以称之为“伪信号”是因为这三个信号是由Shell产生的,而其它的信号是由操作系统产生的)
利用trap命令捕获这三个“伪信号”并输出相关信息是Shell脚本调试的一种重要技巧
信号名称 |
产生条件 |
EXIT |
从函数中退出,或整个脚本执行完毕 |
ERR |
当一条命令返回非零状态码,即命令执行不成功 |
DEBUG |
脚本中的每一条命令执行之前 |
在调试过程中,为了跟踪某些变量的值,我们常常需要在Shell脚本的许多地方插入相同的echo语句来打印相关变量的值,这种做法显得烦琐而笨拙。而利用trap命令捕获DEBUG信号,我们只需要一条trap语句就可以完成对相关变量的全程跟踪
Eg
#!/bin/bash trap 'echo "before execute line:$LINENO,a=$a,b=$b"' DEBUG # LINENO代表行号 a=1 b=1 c=100 sum=0 while : ; do let "sum=a+b" if (( $sum > $c )) then echo "sum:$sum, c:$c" break fi let "a+=1" let "b+=2" let "c-=5" done
从函数退出或脚本结束时,
Shell发出EXIT信号;
当函数或脚本返回非0值时,Shell发出ERR信号
Exit的例子
Eg
#!/bin/bash fun1() { echo "This is an correct function" var=2010 return 0 } trap 'echo"Line:$LINENO,var=$var"' EXIT fun1 #var=2009 echo echo exit 0
err的例子
#!/bin/bash fun2() { echo "This is an error function" var=2010 return 1 } trap 'echo"Line:$LINENO,var=$var"' ERR fun2 ipconfig
(2)使用tee命令
tee命令产生的数据流向很像英文字母T,将一个输出分为两个支流,一个到标准输出,另一个到某输出文件
tee命令的这种特性可以用到Shell脚本的管道及输入输出重定向的调试上,当我们发现由管道连接起来的一系列命令的执行结果并非如预期的那样,就需要逐步检查各条命令的执行结果来定位错误,但因为使用了管道,这些中间结果并不会显示在屏幕上,这给调试带来了困难
obtainIP.sh目标是获得机器的IP地址并存储到某变量之中
localIP=`cat/etc/sysconfig/network-scripts/ifcfg-eth0 | grep 'IPADDR' | cut -d= -f2`
echo "The local IP is: $localIP“
obtainIP.sh目标简洁,但是,不容易发现其中错误,有必要加入tee命令,看清楚中间结果
localIP=`cat/etc/sysconfig/network-scripts/ifcfg-eth0 | tee debug.txt | grep 'IPADDR' | tee-a debug.txt | cut -d= -f2 | tee -a debug.txt`
echo "The local IP is: $localIP"
(3)调试钩子
调试钩子也称为调试块,是源自于高级程序设计语言中的方法。调试钩子实际上是一个if/then结构的代码块,DEBUG变量控制该代码块是否执行,在程序的开发调试阶段,将DEBUG变量设置为TRUE,使其输出调试信息,到了程序交付使用阶段,将DEBUG设置为FALSE,关闭调试钩子,而无需一一删除调试钩子的代码
if [ "$DEBUG" = "true"]
then
echo "Debugging information:"
… #在此可添加其他输出调试信息
fi
eg debugblock.sh
#!/bin/bash DEBUG() { if ["$DEBUG" = "true" ] then $@ fi } a=0 b=2 c=100 DEBUG echo "a=$a b=$b c=$c" while : do DEBUG echo "a=$a b=$b c=$c" if((a >= 10)) then break fi let "a=a+2" let "b=b*2" let "c=c-10" done
运行结果:
anders@anders-virtual-machine:~/code/shell/debug$exportDEBUG=true (要设置DEBUG环境变量)
anders@anders-virtual-machine:~/code/shell/debug$./debugblock.sh
a=0 b=2 c=100
a=0 b=2 c=100
a=2 b=4 c=90
a=4 b=8 c=80
a=6 b=16 c=70
a=8 b=32 c=60
a=10 b=64 c=50
(4)使用shell选项
在众多的Shell选项中,有三个选项可以用于脚本的调试,它们是-n、-x和-c
两种方法用-n选项进行语法检查 仅仅是检查语法并不执行脚本
1 set –n
在shell脚本中开头加上set –n 然后运行
2 运行脚本是的时候使用 sh –n 脚本名
2 sh –x 调试
eg e.sh
#!/bin/bash isroot() { if[ "$UID" -ne 0 ] then return 1 else return 0 fi } echoroot() { isroot if [ "$?" -ne 0 ] then echo "I am not ROOT user!" else echo "ROOT user!" fi } exportPS4='+{$LINENO:${FUNCNAME[0]}:${FUNCNAME[1]}}' echoroot
运行
sh –x e.sh