前言

Linux的shell脚本上手简单但是调试起来就颇为麻烦,二者间的抉择就像编辑器上轻量级与功能丰富的不可兼得一般。在这里提出一些Linux上脚本调试的小技巧,希望有所帮助。

bash自身特性

众所周知,bash相对于dash而言是更重量级的存在,其中一部分便是这种调试辅助信息。以下的特性都是在bash上验证的,dash下无说明便是没有测试过。
了解一个命令的最好办法自然是man手册:man bash
手册本身命令很多,这次只介绍几个我常用的参数:

-v

一边执行脚本,一边将执行过的脚本命令打印。
注意: 脚本命令本身是被定义到标准错误输出打印,而真实结果是在标准输出打印。
在这里插入图片描述

-n

读一遍脚本中的命令但不执行,用于检查脚本中的语法错误。
注意:这里的检查主要是针对语法格式缺少(例如,if语句少了then),但是还有很多是没有检查的(比如,if语句没加空格、函数未定义)。

-x

提供跟踪执行信息,将执行的每一条命令和结果依次打印出来。这个的功能超级强大,将会重点介绍。

  1. 如果有多个脚本嵌套关系,可以进入次级脚本检查(比如a.sh调用b.sh,b.sh调用c.sh,只会显示a.sh和b.sh的内容)

    所以最好是哪个脚本想追踪就在哪个脚本里加上set -x。

  2. 行首显示+,+后面显示经过替换之后的命令行内容,有助于分析实际执行的是什么命令。++代表函数或脚本进一步的调用。

示例:

	root@hik-PC:/tmp# cat test.sh 
	#!/bin/bash
	echo 111 
	SER="/usr/lib/dconf/dconf-service"
	fun(){
	pp=`ps -ef | grep $SER`
	}
	fun
	bash a.sh
	
	root@hik-PC:/tmp# cat a.sh 
	#!/bin/bash
	echo aaa
	dd=`ps -ef |grep a.sh`
	
	root@hik-PC:/tmp# bash -x test.sh 
	+ echo 111
	111
	+ SER=/usr/lib/dconf/dconf-service
	+ fun
	++ ps -ef
	++ grep /usr/lib/dconf/dconf-service
	+ pp='hikvisi+  3348  3016  0 5月13 ?       00:00:00 /usr/lib/dconf/dconf-service
	root     32422 32420  0 11:22 pts/1    00:00:00 grep /usr/lib/dconf/dconf-service'
	+ bash a.sh
	aaa

-x的增强

$LINENO

代表shell脚本的当前行号

$FUNCNAME

函数的名字,它是一个数组变量,其中包含了整个调用链上所有的函数的名字,所以变量${FUNCNAME[0]}代表shell脚本当前正在执行的函数的名字,而变量${FUNCNAME[1]}则代表调用函数${FUNCNAME[0]}的函数的名字,余者可以依此类推。

$PS4

$PS4的值将被显示在“-x”选项输出的每一条命令的前面,在Bash Shell中,缺省的$PS4的值是"+"号。所以默认执行bash -x时前面有“+”号。进一步介绍,可以参照上面我的参考文献。

示例:

	root@hik-PC:/tmp# cat -n test.sh 
	     1	#!/bin/sh
	     2	export PS4='+$LINENO: {${FUNCNAME[0]}}'
	     3	echo 111 
	     4	SER="/usr/lib/dconf/dconf-service"
	     5	fun(){
	     6		pp=`ps -ef | grep $SER`
	     7	}
	     8	funn(){
	     9		fun
	    10	}
	    11	funn
	    12	bash a.sh
	root@hik-PC:/tmp# bash -x test.sh 
	+ export 'PS4=+$LINENO: {${FUNCNAME[0]}}'
	+ PS4='+$LINENO: {${FUNCNAME[0]}}'
	+3: {}echo 111
	111
	+4: {}SER=/usr/lib/dconf/dconf-service
	+11: {}funn
	+9: {funn}fun
	++6: {fun}ps -ef
	++6: {fun}grep /usr/lib/dconf/dconf-service
	+6: {fun}pp='hikvisi+  3348  3016  0 5月13 ?       00:00:00 /usr/lib/dconf/dconf-service
	root      5476  5474  0 13:55 pts/1    00:00:00 grep /usr/lib/dconf/dconf-service'
	+12: {}bash a.sh
	aaa

最后,这些命令有多种实现方式:

  • 命令行:bash -x xx.sh
  • 在脚本开头提供参数#!/bin/bash -x
  • 脚本中,set -x (启用) / set +x (禁用)

trap

定义:用于捕获指定的信号并执行预定义的命令。
语法:trap ‘command’ signal
注意:

  • signal主要使用shell内部的伪信号(exit,err,debug
    debug是脚本中每一条命令执行之前),很多资料也写可以捕获系统信号,但没有测试过。
  • dash没有这种伪信号,所以必须用bash。

示例:

root@hik-PC:/tmp# cat -n trap.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"

root@hik-PC:/tmp# bash trap.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

调试钩子

用途:当测试环境和部署环境不同时,不同的要求和判定能把人折磨疯,利用export DEBUG=true既可以执行判断语句内内容,不用一条条删语句了。
示例:

if [$DEBUG= “true” ]; then
echo “debugging”  #此处可以输出调试信息
fi

bashdb

bashdb是一个类GDB的调试工具,可以完成对shell脚本的断点设置,单步执行,变量观察等许多功能。

使用bashdb进行debug的常用命令:

 1. 列出代码和查询代码类:
    l  列出当前行以下的10行
    \-  列出正在执行的代码行的前面10行
    .  回到正在执行的代码行
    w 列出正在执行的代码行前后的代码
    /pat/ 向后搜索pat
    ?pat?向前搜索pat

 2. Debug控制类:
    h 帮助
    help 命令 得到命令的具体信息
    q 退出bashdb
    x 算数表达式 计算算数表达式的值,并显示出来
    !!空格Shell命令 参数 执行shell命令
    使用bashdb进行debug的常用命令(cont.)
    控制脚本执行类:
    n  执行下一条语句,遇到函数,不进入函数里面执行,将函数当作黑盒
    s n 单步执行n次,遇到函数进入函数里面
    b 行号n 在行号n处设置断点      
    (经验证,bashdb的break设置断点命令必须s、s、c然后到这个断点以后,还得重新设置下一个断点,否则不生效,===>即再次s、s、c才行)
    del 行号n 撤销行号n处的断点
    c 行号n 一直执行到行号n处
    R 重新启动
    Finish 执行到程序最后
    cond n expr 条件断点
Logo

更多推荐