shell脚本调试方法

trap命令

trap命令用于捕获指定的信号并执行预定义的命令:

trap 'command' signal

其中signal是要捕获的信号,command是捕获到指定的信号之后,所要执行的命令,可以是一条或多条合法的shell语句,也可以是一个函数名。
shell脚本在执行时,有三个“伪信号”(之所以称之为“伪信号”是因为由shell产生):

伪信号何时产生
EXIT脚本执行完毕
ERR当一条命令返回非零时(代表命令执行不成功)
DEBUG脚本中每一条命令执行之前

在调试过程中,为了跟踪某些变量的值,我们常常需要在shell脚本的许多地方插入相同的echo语句来打印相关变量的值,这种做法显得烦琐而笨拙。而通过捕获DEBUG信号,我们只需要一条trap语句就可以完成对相关变量的全程跟踪。
以下是一个通过捕获DEBUG信号来跟踪变量的示例程序:

$ cat exp1.sh
     1  trap 'echo "before execute line:$LINENO, a=$a,b=$b,c=$c"' DEBUG
     2  a=1
     3  if [ "$a" -eq 1 ]
     4  then
     5      b=2
     6  else
     7      b=1
     8  fi
     9  c=3
    10  echo "end"
$ bash exp1.sh
before execute line:2, a=,b=,c=
before execute line:3, a=1,b=,c=
before execute line:5, a=1,b=,c=
before execute line:9, a=1,b=2,c=
before execute line:10, a=1,b=2,c=3
end

调试钩子

通过定义一个DEBUG函数可以使植入调试钩子的过程简洁方便,如下面代码所示:

$ cat –n exp2.sh
     1  DEBUG()
     2  {
     3      if [ "$DEBUG" = "true" ]; then
     4          $@  
     5      fi
     6  }
     7  DEBUG="true"
     8  a=1
     9  DEBUG echo "a=$a"
    10  if [ "$a" -eq 1 ]; then
    11      b=2
    12  else
    13      b=1
    14  fi
    15  DEBUG echo "b=$b"
    16  c=3
    17  DEBUG echo "c=$c"
$ bash exp2.sh
a=1
b=2
c=3

shell的执行选项

本节将介绍几个shell常用选项的用法:

-c "string" 从string中读取命令
-n, -o noexec 只读取shell脚本,但不实际执行,用于测试shell脚本是否存在语法错误
-x, -o xtrace 进入跟踪方式,显示所执行的每一条命令
-u, -o nounset 发现变量未赋值便使用,报错并停止执行
-f, -o noglob 不对通配符进行文件名扩展
-v, -o verbose 打印 Shell 接收到的每一行输入
-e, -o errexit 发现命令出错(返回非0),报错并停止执行
-o pipefail 发现通过管道连接的多条命令中任一出错,报错并停止执行(通过管道连接的
		多条命令的返回值是最后一个命令的返回值,所以没法用-e追踪)

shell的执行选项除了可以在启动shell时指定外,亦可在脚本中用set命令来指定。 "set -参数"表示启用某选项,"set +参数"表示关闭某选项,比如set -x/set -o xtrace和set +x/set +o xtrace。

“-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:2, a=,b=,c='
before execute line:2, a=,b=,c=
+ a=1
++ echo 'before execute line:3, a=1,b=,c='
before execute line:3, a=1,b=,c=
+ '[' 1 -eq 1 ']'
++ echo 'before execute line:5, a=1,b=,c='
before execute line:5, a=1,b=,c=
+ b=2
++ echo 'before execute line:9, a=1,b=2,c='
before execute line:9, a=1,b=2,c=
+ c=3
++ echo 'before execute line:10, a=1,b=2,c=3'
before execute line:10, a=1,b=2,c=3
+ echo end
end

在上面的结果中,前面有“+”号的行是shell脚本实际执行的命令,前面有“++”号的行是执行trap机制中指定的命令,其它的行则是输出信息。
有时候我们并不需要在启动时用"-x"选项来跟踪所有的命令行,这时我们可以在脚本中使用set命令。set命令同样可以使用上一节中介绍的调试钩子—DEBUG函数来调用,这样可以避免脚本交付使用时删除这些调试语句的麻烦,如以下脚本片段所示:

DEBUG set -x
要跟踪的程序段
DEBUG set +x

环境变量

PS1是主提示符变量,默认值 [\u@\h \W]\$,显示用户主机名称和工作目录。

一个长命令可以通过在末尾加 \ 使其分行显示,PS2是多行命令的默认提示符,默认值 >

PS3是Shell脚本中使用select时的提示符:

$ cat ps3.sh  
PS3="Select a day (1-4): "
select i in mon tue wed exit
do
	case $i in
		mon) echo "Monday";;
		tue) echo "Tuesday";;
		wed) echo "Wednesday";;
		exit) exit;;
	esac
done
$ bash ps3.sh 
1) mon
2) tue
3) wed
4) exit
Select a day (1-4): 1
Monday
Select a day (1-4): 2
Tuesday
Select a day (1-4): 4

PS4是被显示在“-x”选项输出的每一条命令前面的提示符,默认值 +

LINENO
在脚本里面的行号

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

BASH_SOURCE
脚本名字,是一个数组变量,包含了整个调用链上所有脚本的名字,
${BASH_SOURCE[0]}是当前执行的脚本,${BASH_SOURCE[1]}是调用当前脚本的脚本,依此类推。

BASH_LINENO
每一轮调用对应的行号,是一个数组变量,${BASH_LINENO[$i]}表示${FUNCNAME[$i]}在调用它的
脚本文件${BASH_SOURCE[$i+1]}里面的行号。

修改PS4的值,再执行一次exp2.sh:

$ export PS4='+{$LINENO:${FUNCNAME[0]}} '
$ bash -x exp2.sh
+{1:main} trap 'echo "before execute line:$LINENO, a=$a,b=$b,c=$c"' DEBUG
++{2:main} echo 'before execute line:2, a=,b=,c='
before execute line:2, a=,b=,c=
+{2:main} a=1
++{3:main} echo 'before execute line:3, a=1,b=,c='
before execute line:3, a=1,b=,c=
+{3:main} '[' 1 -eq 1 ']'
++{5:main} echo 'before execute line:5, a=1,b=,c='
before execute line:5, a=1,b=,c=
+{5:main} b=2
++{9:main} echo 'before execute line:9, a=1,b=2,c='
before execute line:9, a=1,b=2,c=
+{9:main} c=3
++{10:main} echo 'before execute line:10, a=1,b=2,c=3'
before execute line:10, a=1,b=2,c=3
+{10:main} echo end
end

参考

Shell脚本调试技术
linux下PS1、PS2、PS3、PS4最全详解
阮一峰 Bash 脚本教程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值