Linux零碎中的Shell剧本调试手艺
来源:长沙电脑培训学校|发布时间:2016-05-21|浏览量:
摘要:本文周全系统地先容了shell剧本调试手艺,包罗应用echo, tee, trap等号令输入关头信息,跟踪变量的值,在剧本中植进调试钩子...本文周全系统地先容了shell剧本调试手艺,包罗应用echo, tee, trap等号令输入关头信息,跟踪变量的值,在剧本中植进调试钩子,应用“-n”选项中止shell剧本的语法查抄,应用“-x”选项完成shell剧本逐条语句的跟踪,巧妙天时用shell的内置变量增强“-x”选项的输入信息等。
一. 媒介
shell编程在unix/linux世界中应用得很是普遍,谙练把握shell编程也是成为一位优良的unix/linux开拓者和系统办理员的必经之路。剧本调试的首要任务就是发现激起剧本缺点的缘由和在剧根源代码中定位产生缺点的行,经常运用的伎俩包罗阐发输入的缺点信息,经由过程在剧本中插手调试语句,输入调试信息来辅佐诊断缺点,支配调试东西等。但与其它初级说话比较,shell诠释器短少响应的调试机制和调试东西的支撑,其输入的缺点信息又常常很不明白,初学者在调试剧本时,除明白用echo语句输入一些信息外,别无它法,而仅仅依托于年夜量的插手echo语句来诊断缺点,确切使人不堪其繁,故稀有初学者抱怨shell剧本太难调试了。本文将系统地先容一些主要的shell剧本调试手艺,但愿能对shell的初学者有所裨益。
本文的方针读者是unix/linux状况下的开拓职员,测试职员和系统办理员,请求读者具有根基的shell编程常识。本文所应用模范在Bash3.1 +Redhat Enterprise Server 4.0下测试经由过程,但所述调试技艺应也一样合用于其它shell。
二. 在shell剧本中输入调试信息
经由过程在法式中插手调试语句把一些关头处所或犯错的处所的相干信息显现出来是最多见的调试伎俩。Shell法式员但凡应用echo(ksh法式员常应用 print)语句输入信息,但仅仅依托echo语句的输入跟踪信息很省事,调试阶段在剧本中插手的年夜量的echo语句在产物托付时还得再费劲逐一删除。针对这个标题,本节首要先容一些若何便利有用的输入调试信息的方式。
1. 应用trap号令
trap号令用于捕捉指定的旌旗灯号并实行预界说的号令。
其根基的语法是:
trap command signal
此中signal是要捕捉的旌旗灯号,command是捕捉到指定的旌旗灯号以后,所要实行的号令。能够用kill –l号令看到系统中全数可用的旌旗灯号名,捕捉旌旗灯号后所实行的号令可所以任何一条或多条合理的shell语句,也可以是一个函数名。
shell剧本在实行时,会发作三个所谓的“伪旌旗灯号”,(之所以称之为“伪旌旗灯号”是由于这三个旌旗灯号是由shell发作的,而其它的旌旗灯号是由支配系统发作的),经由过程应用trap号令捕捉这三个“伪旌旗灯号”并输入相干信息对换试很是有帮助。
表 1. shell伪旌旗灯号
旌旗灯号名 什么时分发作
EXIT 从一个函数中参加或全部剧本实行终了
ERR 当一条号令前往非零情况时(代表号令实行不胜利)
DEBUG 剧本中每条号令实行之前
经由过程捕捉EXIT旌旗灯号,我们能够在shell剧本中缀实行或从函数中参加时,输入某些想要跟踪的变量的值,并由此来断定剧本的实行情况和犯错缘由,其应用方式是:
trap command EXIT 或 trap command 0
经由过程捕捉ERR旌旗灯号,我们能够便利的追踪实行不胜利的号令或函数,并输入相干的调试信息,以下是一个捕捉ERR旌旗灯号的示例法式,此中的$LINENO是一个shell的内置变量,代表shell剧本确当前行号。
$ cat -n exp1.sh 1 ERRTRAP() 2 { 3 echo [LINE:$1] Error: Command or function exited with status $? 4 } 5 foo() 6 { 7 return 1; 8 } 9 trap ERRTRAP $LINENO ERR 10 abc 11 foo
其输入成果以下:
$ sh exp1.sh exp1.sh: line 10: abc: command not found [LINE:10] Error: Command or function exited with status 127 [LINE:11] Error: Command or function exited with status 1
在调试进程中,为了跟踪某些变量的值,我们经常需求在shell剧本的很多处所拔出不异的echo语句来打印相干变量的值,这类做法显得烦琐而拙笨。而经由过程捕捉DEBUG旌旗灯号,我们只需求一条trap语句便能够完成对相干变量的全程跟踪。
以下是一个经由过程捕捉DEBUG旌旗灯号来跟踪变量的示例法式:
$ cat –n exp2.sh 1 #!/bin/bash 2 trap echo “before execute line:$LINENO, a=$a,b=$b,c=$c” DEBUG 3 a=1 4 if [ $a -eq 1 ] 5 then 6 b=2 7 else 8 b=1 9 fi 10 c=3 11 echo end
其输入成果以下:
$ sh exp2.sh before execute line:3, a=,b=,c= before execute line:4, a=1,b=,c= before execute line:6, a=1,b=,c= before execute line:10, a=1,b=2,c= before execute line:11, a=1,b=2,c=3 end
从运转成果中能够分明的看到每实行一条号令以后,相干变量的值的转变。同时,从运转成果中打印出来的行号来阐发,能够看到全部剧本的实行轨迹,能够或许断定出哪些前提分支实行了,哪些前提分支没有实行。
2. 应用tee号令
在shell剧本中管道和输出输入重定向应用得很是多,在管道的感化下,一些号令的实行成果直接成了下一条号令的输出。若是我们发现由管道毗邻起来的一批号令的实行成果并不是如预期的那样,就需求渐渐查抄各条号令的实行成果来断定标题出在哪儿,但由于应用了管道,这些中心成果其实不会显现在屏幕上,给调试带来了坚苦,此时我们便能够借助于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便可取得精确的成果。
细致到上述的s cript例子,我们或许其实不需求tee号令的帮助,好比我们能够分段实行由管道毗邻起来的各条号令并检查各号令的输入成果来诊断缺点,但在一些复杂的shell剧本中,这些由管道毗邻起来的号令可以又依托于剧本中界说的一些其它变量,这时分我们想要在提示符上去分段运转各条号令就会很是省事了,俭朴地在管道之间拔出一条tee号令来检查中心成果会更便利一些。
3. 应用 调试钩子
在C说话法式中,我们常常应用DEBUG宏来节制是不是要输入调试信息,在shell剧本中我们一样可运用多么的机制,以下列代码所示:
if [ “$DEBUG” = “true” ]; then echo “debugging” #此处能够输入调试信息 fi
多么的代码块但凡称之为“调试钩子”或 “调试块”。在调试钩子外部能够输入任何您想输入的调试信息,应用调试钩子的好处是它是能够经由过程DEBUG变量来节制的,在剧本的开拓调试阶段,能够先实行export DEBUG=true号令翻开调试钩子,使其输入调试信息,而在把剧本托付应用时,也无需再省事把剧本中的调试语句逐一删除。
若是在每处需求输入调试信息的处所均应用if语句来断定DEBUG变量的值,仍是显得比力繁琐,经由过程界说一个DEBUG函数可使植进调试钩子的进程更精练便利,以下面代码所示:
$ cat –n exp3.sh 1 DEBUG() 2 { 3 if [ $DEBUG = true ]; then 4 $@ 5 fi 6 } 7 a=1 8 DEBUG echo a=$a 9 if [ $a -eq 1 ] 10 then 11 b=2 12 else 13 b=1 14 fi 15 DEBUG echo b=$b 16 c=3 17 DEBUG echo c=$c
在下面所示的DEBUG函数中,会实行任何传给它的号令,而且这个实行进程是能够经由过程DEBUG变量的值来节制的,我们能够把一切跟调试有关的号令都作为DEBUG函数的参数来挪用,很是的便利。
三. 应用shell的实行选项
上一节所述的调试伎俩是经由过程点窜shell剧本的源代码,令其输入相干的调试信息来定位缺点的,那有无不点窜源代码来调试shell剧本的方式呢?谜底就是应用shell的实行选项,本节将先容一些经常运用选项的用法:
-n 只读取shell剧本,但不理想实行
-x 进进跟踪格式,显现所实行的每条号令
-c string 从strings中读取号令
“-n”可用于测试shell剧本是不是存在语法缺点,但不会理想实行号令。在shell剧本编写完成以后,理想实行之前,起首应用“-n”选项来测试剧本是不是存在语法缺点是一个很好的习气。由于某些shell剧本在实行时会对系统状况发作影响,好比天生或移动文件等,若是在理想实行才发现语法缺点,您不能不手工做一些系统状况的恢停工作才干连续测试这个剧本。
“-c”选项使shell诠释器从一个字符串中而不是从一个文件中读取并实行shell号令。当需求权且测试一小段剧本的实行成果时,可运用这个选项,以下所示:
sh -c a=1;b=2;let c=$a+$b;echo c=$c
-x 选项可用来跟踪剧本的实行,是调试shell剧本的强无力东西。“-x”选项使shell在实行剧本的进程中把它理想实行的每个号令行显现出来,而且外行首显现一个 + 号。 + 号前面显现的是颠末了变量替代以后的号令行的内容,有助于阐发理想实行的是甚么号令。 “-x”选项应用起来俭朴便利,能够轻松关于年夜大都的shell调试任务,应把其看成首选的调试伎俩。
若是把本文后面所述的trap ‘command’ DEBUG机制与“-x”选项连系起来,我们便能够既输入理想实行的每条号令,又逐行跟踪相干变量的值,对换试相当有帮助。
仍之前面所述的exp2.sh为例,此刻加上“-x”选项来实行它:
$ sh –x exp2.sh + trap echo before execute line:$LINENO, a=$a,b=$b,c=$c DEBUG ++ echo before execute line:3, a=,b=,c= before execute line:3, a=,b=,c= + a=1 ++ echo before execute line:4, a=1,b=,c= before execute line:4, a=1,b=,c= + [ 1 -eq 1 ] ++ echo before execute line:6, a=1,b=,c= before execute line:6, a=1,b=,c= + b=2 ++ echo before execute line:10, a=1,b=2,c= before execute line:10, a=1,b=2,c= + c=3 ++ echo before execute line:11, a=1,b=2,c=3 before execute line:11, a=1,b=2,c=3 + echo end end
在下面的成果中,后面有“+”号的行是shell剧本理想实行的号令,后面有“++”号的行是实行trap机制中指定的号令,其它的行则是输入信息。
shell的实行选项除能够在启动shell时指定外,亦可在剧本顶用set号令来指定。 set -参数 暗示启用某选项, set +参数 暗示封锁某选项。有时辰我们其实不需求在启动时用 -x 选项来跟踪一切的号令行,这时分我们能够在剧本中应用set号令,如以下剧本片断所示:
set -x #启动 -x 选项 要跟踪的法式段 set +x #封锁 -x 选项
set号令一样可运用上一节中先容的调试钩子—DEBUG函数来挪用,多么能够免剧本托付应用时删除这些调试语句的省事,如以下剧本片断所示:
DEBUG set -x #启动 -x 选项 要跟踪的法式段 DEBUG set +x #封锁 -x 选项
四. 对 -x 选项的增强
-x 实行选项是今朝最经常运用的跟踪和调试shell剧本的伎俩,但其输入的调试信息仅限于中止变量替代以后的每条理想实行的号令和行首的一个 + 号提示符,居然连行号多么的主要信息都没有,对复杂的shell剧本的调试来讲,仍是很是的不便利。荣幸的是,我们能够巧妙天时用shell内置的一些状况变量来增强 -x 选项的输入信息,上面先先容几个shell内置的状况变量:
$LINENO
代表shell剧本确当前行号,近似于C说话中的内置宏__LINE__
$FUNCNAME
函数的名字,近似于C说话中的内置宏__func__,但宏__func__ 只能代表以后地点的函数名,而$FUNCNAME的功用更壮大,它是一个数组变量,此中包括了全部挪用链上一切的函数的名字,故变量${FUNCNAME [0]}代表shell剧本以后正在实行的函数的名字,而变量${FUNCNAME[1]}则代表挪用函数${FUNCNAME[0]}的函数的名字,余者能够依此类推。
$PS4
主提示符变量$PS1和第二级提示符变量$PS2比力稀有,但很少有人注重到第四级提示符变量$PS4的感化。我们明白应用“-x”实行选项将会显现shell剧本中每条理想实行过的号令,而$PS4的值将被显现在“-x”选项输入的每条号令的后面。在Bash Shell中,缺省的$PS4的值是 + 号。(此刻明白为何应用 -x 选项时,输入的号令后面有一个 + 号了吧?)。
支配$PS4这一特征,经由过程应用一些内置变量来重界说$PS4的值,我们便能够增强 -x 选项的输入信息。例如先实行export PS4= +{$LINENO:${FUNCNAME[0]}} , 然后再应用“-x”选项来实行剧本,就能够在每条理想实行的号令后面显现其行号和所属的函数名。
以下是一个存在bug的shell剧本的示例,本文将用此剧原本示范若何用“-n”和增强的“-x”实行选项来调试shell剧本。这个剧本中界说了一个函数isRoot(),用于断定以后用户能否是root用户,若是不是,则中缀剧本的实行
$ cat –n exp4.sh 1 #!/bin/bash 2 isRoot() 3 { 4 if [ $UID -ne 0 ] 5 return 1 6 else 7 return 0 8 fi 9 } 10 isRoot 11 if [ $? -ne 0 ] 12 then 13 echo Must be root to run this s cript 14 exit 1 15 else 16 echo welcome root user 17 #do something 18 fi
起首实行sh –n exp4.sh来中止语法查抄,输入以下:
$ sh –n exp4.sh exp4.sh: line 6: syntax error near unexpected token `else exp4.sh: line 6: ` else
发现了一个语法缺点,经由过程仔细查抄第6行前后的号令,我们发现是第4行的if语句贫乏then关头字引发的(写惯了C法式的人很随便犯这个缺点)。我们能够把第4行点窜为if [ $UID -ne 0 ]; then来修改这个缺点。再次运转sh –n exp4.sh来中止语法查抄,没有再陈说缺点。接上去便能够理想实行这个剧本了,实行成果以下:
$ sh exp4.sh exp2.sh: line 11: [1: command not found welcome root user
固然剧本没有语法缺点了,在实行时却又陈说了缺点。缺点信息还很是奇特“[1: command not found”。此刻我们能够尝尝定制$PS4的值,并应用“-x”选项来跟踪:
$ export PS4= +{$LINENO:${FUNCNAME[0]}} $ sh –x exp4.sh +{10:} isRoot +{4:isRoot} [ 503 -ne 0 ] +{5:isRoot} return 1 +{11:} [1 -ne 0 ] exp4.sh: line 11: [1: command not found +{16:} echo welcome root user welcome root user
从输入成果中,我们能够看到剧本理想被实行的语句,该语句的行号和所属的函数名也被打印出来,从中能够明晰的阐发出剧本的实行轨迹和所挪用的函数的外部实行环境。由于实行时是第11行报错,这是一个if语句,我们对照阐发一下同为if语句的第4行的跟踪成果:
+{4:isRoot} [ 503 -ne 0 ] +{11:} [1 -ne 0 ]
可知由于第11行的[号前面贫乏了一个空格,致使[号与紧挨它的变量$?的值1被shell诠释器看做了一个全体,并试着把这个全体视为一个号令来实行,故有“[1: command not found”多么的缺点提示。只需在[号前面拔出一个空格就一切正常了。
shell中还有其它一些对换试有帮助的内置变量,好比在Bash Shell中还有BASH_SOURCE, BASH_SUBSHELL等一批对换试有帮助的内置变量,您能够经由过程man sh或man bash来检查,然后依照您的调试目的,应用这些内置变量来定制$PS4,从而抵达增强“-x”选项的输入信息的目的。
五. 总结
此刻让我们来总结一下调试shell剧本的进程:
起首应用“-n”选项查抄语法缺点,然后应用“-x”选项跟踪剧本的实行,应用“-x”选项之前,别忘了先定制PS4变量的值来增强“-x”选项的输入信息,最少应当令其输入行号信息(先实行export PS4= +[$LINENO] ,更日积月累的法子是将这条语句加到您用户主目次的.bash_profile文件中往),这将使你的调试之旅更轻松。也可以支配trap,调试钩子等伎俩输入关头调试信息,疾速减少排查缺点的范围,并在剧本中应用“set -x”及“set +x”对某些代码块中止重点跟踪。多么多种伎俩齐下,置信您已能够比力轻松地抓出您的shell剧本中的臭虫了。若是您的剧本充足复杂,还需求更强的调试才干,可运用shell调试器bashdb,这是一个近似于GDB的调试东西,能够完成对shell剧本的断点设置,单步实行,变量不雅察等很多功用,应用bashdb对阅读和理解复杂的shell剧本也会年夜有裨益。关于bashdb的安装和应用,不属于本文范围,您可参阅http: //bashdb.sourceforge.net/上的文档并下载试用。
本文周全系统地先容了shell剧本调试手艺,包罗应用echo, tee, trap等号令输入关头信息,跟踪变量的值,在剧本中植进调试钩子,应用“-n”选项中止shell剧本的语法查抄,应用“-x”选项完成shell剧本逐条语句的跟踪,巧妙天时用shell的内置变量增强“-x”选项的输入信息等。
一. 媒介
shell编程在unix/linux世界中应用得很是普遍,谙练把握shell编程也是成为一位优良的unix/linux开拓者和系统办理员的必经之路。剧本调试的首要任务就是发现激起剧本缺点的缘由和在剧根源代码中定位产生缺点的行,经常运用的伎俩包罗阐发输入的缺点信息,经由过程在剧本中插手调试语句,输入调试信息来辅佐诊断缺点,支配调试东西等。但与其它初级说话比较,shell诠释器短少响应的调试机制和调试东西的支撑,其输入的缺点信息又常常很不明白,初学者在调试剧本时,除明白用echo语句输入一些信息外,别无它法,而仅仅依托于年夜量的插手echo语句来诊断缺点,确切使人不堪其繁,故稀有初学者抱怨shell剧本太难调试了。本文将系统地先容一些主要的shell剧本调试手艺,但愿能对shell的初学者有所裨益。
本文的方针读者是unix/linux状况下的开拓职员,测试职员和系统办理员,请求读者具有根基的shell编程常识。本文所应用模范在Bash3.1 +Redhat Enterprise Server 4.0下测试经由过程,但所述调试技艺应也一样合用于其它shell。
二. 在shell剧本中输入调试信息
经由过程在法式中插手调试语句把一些关头处所或犯错的处所的相干信息显现出来是最多见的调试伎俩。Shell法式员但凡应用echo(ksh法式员常应用 print)语句输入信息,但仅仅依托echo语句的输入跟踪信息很省事,调试阶段在剧本中插手的年夜量的echo语句在产物托付时还得再费劲逐一删除。针对这个标题,本节首要先容一些若何便利有用的输入调试信息的方式。
1. 应用trap号令
trap号令用于捕捉指定的旌旗灯号并实行预界说的号令。
其根基的语法是:
trap command signal
此中signal是要捕捉的旌旗灯号,command是捕捉到指定的旌旗灯号以后,所要实行的号令。能够用kill –l号令看到系统中全数可用的旌旗灯号名,捕捉旌旗灯号后所实行的号令可所以任何一条或多条合理的shell语句,也可以是一个函数名。
shell剧本在实行时,会发作三个所谓的“伪旌旗灯号”,(之所以称之为“伪旌旗灯号”是由于这三个旌旗灯号是由shell发作的,而其它的旌旗灯号是由支配系统发作的),经由过程应用trap号令捕捉这三个“伪旌旗灯号”并输入相干信息对换试很是有帮助。
表 1. shell伪旌旗灯号
旌旗灯号名 什么时分发作
EXIT 从一个函数中参加或全部剧本实行终了
ERR 当一条号令前往非零情况时(代表号令实行不胜利)
DEBUG 剧本中每条号令实行之前
经由过程捕捉EXIT旌旗灯号,我们能够在shell剧本中缀实行或从函数中参加时,输入某些想要跟踪的变量的值,并由此来断定剧本的实行情况和犯错缘由,其应用方式是:
trap command EXIT 或 trap command 0
经由过程捕捉ERR旌旗灯号,我们能够便利的追踪实行不胜利的号令或函数,并输入相干的调试信息,以下是一个捕捉ERR旌旗灯号的示例法式,此中的$LINENO是一个shell的内置变量,代表shell剧本确当前行号。
$ cat -n exp1.sh 1 ERRTRAP() 2 { 3 echo [LINE:$1] Error: Command or function exited with status $? 4 } 5 foo() 6 { 7 return 1; 8 } 9 trap ERRTRAP $LINENO ERR 10 abc 11 foo
其输入成果以下:
$ sh exp1.sh exp1.sh: line 10: abc: command not found [LINE:10] Error: Command or function exited with status 127 [LINE:11] Error: Command or function exited with status 1
在调试进程中,为了跟踪某些变量的值,我们经常需求在shell剧本的很多处所拔出不异的echo语句来打印相干变量的值,这类做法显得烦琐而拙笨。而经由过程捕捉DEBUG旌旗灯号,我们只需求一条trap语句便能够完成对相干变量的全程跟踪。
以下是一个经由过程捕捉DEBUG旌旗灯号来跟踪变量的示例法式:
$ cat –n exp2.sh 1 #!/bin/bash 2 trap echo “before execute line:$LINENO, a=$a,b=$b,c=$c” DEBUG 3 a=1 4 if [ $a -eq 1 ] 5 then 6 b=2 7 else 8 b=1 9 fi 10 c=3 11 echo end
其输入成果以下:
$ sh exp2.sh before execute line:3, a=,b=,c= before execute line:4, a=1,b=,c= before execute line:6, a=1,b=,c= before execute line:10, a=1,b=2,c= before execute line:11, a=1,b=2,c=3 end
从运转成果中能够分明的看到每实行一条号令以后,相干变量的值的转变。同时,从运转成果中打印出来的行号来阐发,能够看到全部剧本的实行轨迹,能够或许断定出哪些前提分支实行了,哪些前提分支没有实行。
2. 应用tee号令
在shell剧本中管道和输出输入重定向应用得很是多,在管道的感化下,一些号令的实行成果直接成了下一条号令的输出。若是我们发现由管道毗邻起来的一批号令的实行成果并不是如预期的那样,就需求渐渐查抄各条号令的实行成果来断定标题出在哪儿,但由于应用了管道,这些中心成果其实不会显现在屏幕上,给调试带来了坚苦,此时我们便能够借助于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便可取得精确的成果。
细致到上述的s cript例子,我们或许其实不需求tee号令的帮助,好比我们能够分段实行由管道毗邻起来的各条号令并检查各号令的输入成果来诊断缺点,但在一些复杂的shell剧本中,这些由管道毗邻起来的号令可以又依托于剧本中界说的一些其它变量,这时分我们想要在提示符上去分段运转各条号令就会很是省事了,俭朴地在管道之间拔出一条tee号令来检查中心成果会更便利一些。
3. 应用 调试钩子
在C说话法式中,我们常常应用DEBUG宏来节制是不是要输入调试信息,在shell剧本中我们一样可运用多么的机制,以下列代码所示:
if [ “$DEBUG” = “true” ]; then echo “debugging” #此处能够输入调试信息 fi
多么的代码块但凡称之为“调试钩子”或 “调试块”。在调试钩子外部能够输入任何您想输入的调试信息,应用调试钩子的好处是它是能够经由过程DEBUG变量来节制的,在剧本的开拓调试阶段,能够先实行export DEBUG=true号令翻开调试钩子,使其输入调试信息,而在把剧本托付应用时,也无需再省事把剧本中的调试语句逐一删除。
若是在每处需求输入调试信息的处所均应用if语句来断定DEBUG变量的值,仍是显得比力繁琐,经由过程界说一个DEBUG函数可使植进调试钩子的进程更精练便利,以下面代码所示:
$ cat –n exp3.sh 1 DEBUG() 2 { 3 if [ $DEBUG = true ]; then 4 $@ 5 fi 6 } 7 a=1 8 DEBUG echo a=$a 9 if [ $a -eq 1 ] 10 then 11 b=2 12 else 13 b=1 14 fi 15 DEBUG echo b=$b 16 c=3 17 DEBUG echo c=$c
在下面所示的DEBUG函数中,会实行任何传给它的号令,而且这个实行进程是能够经由过程DEBUG变量的值来节制的,我们能够把一切跟调试有关的号令都作为DEBUG函数的参数来挪用,很是的便利。
三. 应用shell的实行选项
上一节所述的调试伎俩是经由过程点窜shell剧本的源代码,令其输入相干的调试信息来定位缺点的,那有无不点窜源代码来调试shell剧本的方式呢?谜底就是应用shell的实行选项,本节将先容一些经常运用选项的用法:
-n 只读取shell剧本,但不理想实行
-x 进进跟踪格式,显现所实行的每条号令
-c string 从strings中读取号令
“-n”可用于测试shell剧本是不是存在语法缺点,但不会理想实行号令。在shell剧本编写完成以后,理想实行之前,起首应用“-n”选项来测试剧本是不是存在语法缺点是一个很好的习气。由于某些shell剧本在实行时会对系统状况发作影响,好比天生或移动文件等,若是在理想实行才发现语法缺点,您不能不手工做一些系统状况的恢停工作才干连续测试这个剧本。
“-c”选项使shell诠释器从一个字符串中而不是从一个文件中读取并实行shell号令。当需求权且测试一小段剧本的实行成果时,可运用这个选项,以下所示:
sh -c a=1;b=2;let c=$a+$b;echo c=$c
-x 选项可用来跟踪剧本的实行,是调试shell剧本的强无力东西。“-x”选项使shell在实行剧本的进程中把它理想实行的每个号令行显现出来,而且外行首显现一个 + 号。 + 号前面显现的是颠末了变量替代以后的号令行的内容,有助于阐发理想实行的是甚么号令。 “-x”选项应用起来俭朴便利,能够轻松关于年夜大都的shell调试任务,应把其看成首选的调试伎俩。
若是把本文后面所述的trap ‘command’ DEBUG机制与“-x”选项连系起来,我们便能够既输入理想实行的每条号令,又逐行跟踪相干变量的值,对换试相当有帮助。
仍之前面所述的exp2.sh为例,此刻加上“-x”选项来实行它:
$ sh –x exp2.sh + trap echo before execute line:$LINENO, a=$a,b=$b,c=$c DEBUG ++ echo before execute line:3, a=,b=,c= before execute line:3, a=,b=,c= + a=1 ++ echo before execute line:4, a=1,b=,c= before execute line:4, a=1,b=,c= + [ 1 -eq 1 ] ++ echo before execute line:6, a=1,b=,c= before execute line:6, a=1,b=,c= + b=2 ++ echo before execute line:10, a=1,b=2,c= before execute line:10, a=1,b=2,c= + c=3 ++ echo before execute line:11, a=1,b=2,c=3 before execute line:11, a=1,b=2,c=3 + echo end end
在下面的成果中,后面有“+”号的行是shell剧本理想实行的号令,后面有“++”号的行是实行trap机制中指定的号令,其它的行则是输入信息。
shell的实行选项除能够在启动shell时指定外,亦可在剧本顶用set号令来指定。 set -参数 暗示启用某选项, set +参数 暗示封锁某选项。有时辰我们其实不需求在启动时用 -x 选项来跟踪一切的号令行,这时分我们能够在剧本中应用set号令,如以下剧本片断所示:
set -x #启动 -x 选项 要跟踪的法式段 set +x #封锁 -x 选项
set号令一样可运用上一节中先容的调试钩子—DEBUG函数来挪用,多么能够免剧本托付应用时删除这些调试语句的省事,如以下剧本片断所示:
DEBUG set -x #启动 -x 选项 要跟踪的法式段 DEBUG set +x #封锁 -x 选项
四. 对 -x 选项的增强
-x 实行选项是今朝最经常运用的跟踪和调试shell剧本的伎俩,但其输入的调试信息仅限于中止变量替代以后的每条理想实行的号令和行首的一个 + 号提示符,居然连行号多么的主要信息都没有,对复杂的shell剧本的调试来讲,仍是很是的不便利。荣幸的是,我们能够巧妙天时用shell内置的一些状况变量来增强 -x 选项的输入信息,上面先先容几个shell内置的状况变量:
$LINENO
代表shell剧本确当前行号,近似于C说话中的内置宏__LINE__
$FUNCNAME
函数的名字,近似于C说话中的内置宏__func__,但宏__func__ 只能代表以后地点的函数名,而$FUNCNAME的功用更壮大,它是一个数组变量,此中包括了全部挪用链上一切的函数的名字,故变量${FUNCNAME [0]}代表shell剧本以后正在实行的函数的名字,而变量${FUNCNAME[1]}则代表挪用函数${FUNCNAME[0]}的函数的名字,余者能够依此类推。
$PS4
主提示符变量$PS1和第二级提示符变量$PS2比力稀有,但很少有人注重到第四级提示符变量$PS4的感化。我们明白应用“-x”实行选项将会显现shell剧本中每条理想实行过的号令,而$PS4的值将被显现在“-x”选项输入的每条号令的后面。在Bash Shell中,缺省的$PS4的值是 + 号。(此刻明白为何应用 -x 选项时,输入的号令后面有一个 + 号了吧?)。
支配$PS4这一特征,经由过程应用一些内置变量来重界说$PS4的值,我们便能够增强 -x 选项的输入信息。例如先实行export PS4= +{$LINENO:${FUNCNAME[0]}} , 然后再应用“-x”选项来实行剧本,就能够在每条理想实行的号令后面显现其行号和所属的函数名。
以下是一个存在bug的shell剧本的示例,本文将用此剧原本示范若何用“-n”和增强的“-x”实行选项来调试shell剧本。这个剧本中界说了一个函数isRoot(),用于断定以后用户能否是root用户,若是不是,则中缀剧本的实行
$ cat –n exp4.sh 1 #!/bin/bash 2 isRoot() 3 { 4 if [ $UID -ne 0 ] 5 return 1 6 else 7 return 0 8 fi 9 } 10 isRoot 11 if [ $? -ne 0 ] 12 then 13 echo Must be root to run this s cript 14 exit 1 15 else 16 echo welcome root user 17 #do something 18 fi
起首实行sh –n exp4.sh来中止语法查抄,输入以下:
$ sh –n exp4.sh exp4.sh: line 6: syntax error near unexpected token `else exp4.sh: line 6: ` else
发现了一个语法缺点,经由过程仔细查抄第6行前后的号令,我们发现是第4行的if语句贫乏then关头字引发的(写惯了C法式的人很随便犯这个缺点)。我们能够把第4行点窜为if [ $UID -ne 0 ]; then来修改这个缺点。再次运转sh –n exp4.sh来中止语法查抄,没有再陈说缺点。接上去便能够理想实行这个剧本了,实行成果以下:
$ sh exp4.sh exp2.sh: line 11: [1: command not found welcome root user
固然剧本没有语法缺点了,在实行时却又陈说了缺点。缺点信息还很是奇特“[1: command not found”。此刻我们能够尝尝定制$PS4的值,并应用“-x”选项来跟踪:
$ export PS4= +{$LINENO:${FUNCNAME[0]}} $ sh –x exp4.sh +{10:} isRoot +{4:isRoot} [ 503 -ne 0 ] +{5:isRoot} return 1 +{11:} [1 -ne 0 ] exp4.sh: line 11: [1: command not found +{16:} echo welcome root user welcome root user
从输入成果中,我们能够看到剧本理想被实行的语句,该语句的行号和所属的函数名也被打印出来,从中能够明晰的阐发出剧本的实行轨迹和所挪用的函数的外部实行环境。由于实行时是第11行报错,这是一个if语句,我们对照阐发一下同为if语句的第4行的跟踪成果:
+{4:isRoot} [ 503 -ne 0 ] +{11:} [1 -ne 0 ]
可知由于第11行的[号前面贫乏了一个空格,致使[号与紧挨它的变量$?的值1被shell诠释器看做了一个全体,并试着把这个全体视为一个号令来实行,故有“[1: command not found”多么的缺点提示。只需在[号前面拔出一个空格就一切正常了。
shell中还有其它一些对换试有帮助的内置变量,好比在Bash Shell中还有BASH_SOURCE, BASH_SUBSHELL等一批对换试有帮助的内置变量,您能够经由过程man sh或man bash来检查,然后依照您的调试目的,应用这些内置变量来定制$PS4,从而抵达增强“-x”选项的输入信息的目的。
五. 总结
此刻让我们来总结一下调试shell剧本的进程:
起首应用“-n”选项查抄语法缺点,然后应用“-x”选项跟踪剧本的实行,应用“-x”选项之前,别忘了先定制PS4变量的值来增强“-x”选项的输入信息,最少应当令其输入行号信息(先实行export PS4= +[$LINENO] ,更日积月累的法子是将这条语句加到您用户主目次的.bash_profile文件中往),这将使你的调试之旅更轻松。也可以支配trap,调试钩子等伎俩输入关头调试信息,疾速减少排查缺点的范围,并在剧本中应用“set -x”及“set +x”对某些代码块中止重点跟踪。多么多种伎俩齐下,置信您已能够比力轻松地抓出您的shell剧本中的臭虫了。若是您的剧本充足复杂,还需求更强的调试才干,可运用shell调试器bashdb,这是一个近似于GDB的调试东西,能够完成对shell剧本的断点设置,单步实行,变量不雅察等很多功用,应用bashdb对阅读和理解复杂的shell剧本也会年夜有裨益。关于bashdb的安装和应用,不属于本文范围,您可参阅http: //bashdb.sourceforge.net/上的文档并下载试用。
扫码关注微信公众号了解更多详情
跟技术大咖,专业导师一起交流学习