前言

熟练掌握shell编程和调试是成为一名优秀的unix/linux开发者和系统管理员的必经之路

常用的调试手段

  • 分析输出的错误信息
  • 在脚本中加入调试语句,输出调试信息
  • 利用调试工具

但与其它高级语言相比,shell解释器缺乏相应的调试机制和调试工具的支持,其输出的错误信息又往往很不明确

调试方法

trap命令

trap <command> <signal>

系统信号

[root@zhujipeng test]# kill -l
 1) SIGHUP     2) SIGINT     3) SIGQUIT     4) SIGILL     5) SIGTRAP
 6) SIGABRT     7) SIGBUS     8) SIGFPE     9) SIGKILL    10) SIGUSR1
11) SIGSEGV    12) SIGUSR2    13) SIGPIPE    14) SIGALRM    15) SIGTERM
16) SIGSTKFLT    17) SIGCHLD    18) SIGCONT    19) SIGSTOP    20) SIGTSTP
21) SIGTTIN    22) SIGTTOU    23) SIGURG    24) SIGXCPU    25) SIGXFSZ
26) SIGVTALRM    27) SIGPROF    28) SIGWINCH    29) SIGIO    30) SIGPWR
31) SIGSYS    34) SIGRTMIN    35) SIGRTMIN+1    36) SIGRTMIN+2    37) SIGRTMIN+3
38) SIGRTMIN+4    39) SIGRTMIN+5    40) SIGRTMIN+6    41) SIGRTMIN+7    42) SIGRTMIN+8
43) SIGRTMIN+9    44) SIGRTMIN+10    45) SIGRTMIN+11    46) SIGRTMIN+12    47) SIGRTMIN+13
48) SIGRTMIN+14    49) SIGRTMIN+15    50) SIGRTMAX-14    51) SIGRTMAX-13    52) SIGRTMAX-12
53) SIGRTMAX-11    54) SIGRTMAX-10    55) SIGRTMAX-9    56) SIGRTMAX-8    57) SIGRTMAX-7
58) SIGRTMAX-6    59) SIGRTMAX-5    60) SIGRTMAX-4    61) SIGRTMAX-3    62) SIGRTMAX-2
63) SIGRTMAX-1    64) SIGRTMAX
系统信号 说明
SIGHUP 用户终端连接(正常或非正常)结束时发出
SIGINT 进程终止(interrupt)信号, 结束前台进程
SIGTERM 进程终止(terminate)信号,进程平稳结束
SIGKILL 进程终止(kill)信号,进程立即结束,不能被捕获
SIGUSR1 留给用户使用
SIGUSR2 留给用户使用

SIGTERM、SIGKILL、SIGINT和SIGQUIT的区别参考这里

伪信号 说明
EXIT 从一个函数中退出或整个脚本执行完毕发出
ERR 当一条命令返回非零状态时(代表命令执行不成功)发出
DEBUG 脚本中每一条命令执行之前发出

“伪信号”是因为这三个信号是由shell产生的

[root@zhujipeng test]# cat test.sh
error_trap() {
  echo "[Line $1] Error: command or function exited with status $?"
}

exit_trap() {
  echo "[Line $1] INFO: exit method"
}

foo() {
  false
}

bar() {
  for i in {1..10}
  do
    sleep 1
  done
}

trap 'error_trap $LINENO' ERR
trap 'exit_trap $LINENO' EXIT
trap 'echo abort by user $USER; exit 1' INT

abc
foo
bar
[root@zhujipeng test]# sh test.sh
test.sh: line 24: abc: command not found
[Line 24] Error: command or function exited with status 127
[Line 10] Error: command or function exited with status 1
^Cabort by user
[Line 1] INFO: exit method

tee命令

管道的中间结果并不会显示在屏幕上,需要使用 tee 命令输出到文件观察

[root@zhujipeng test]# cat test.sh
ifconfig | grep 'inet addr:' | grep -v '127.0.0.1'
ifconfig | grep 'inet addr:' | tee /tmp/test.txt | grep -v '127.0.0.1'

echo 'content of test.txt'
cat /tmp/test.txt
[root@zhujipeng test]# sh test.sh
          inet addr:172.17.0.2  Bcast:0.0.0.0  Mask:255.255.0.0
          inet addr:172.17.0.2  Bcast:0.0.0.0  Mask:255.255.0.0
content of test.txt
          inet addr:172.17.0.2  Bcast:0.0.0.0  Mask:255.255.0.0
          inet addr:127.0.0.1  Mask:255.0.0.0

勾子函数

[root@zhujipeng test]# cat test.sh
debug() {
  [ x"$DEBUG" = x"true" ] && echo $*
}

echo debug
debug haha hehe
[root@zhujipeng test]# sh test.sh
debug
[root@zhujipeng test]# DEBUG='true' && sh test.sh
debug

调试开关

shell 命令有一些选项可以帮助调试

|选项 | 说明 | |-n | 只读取脚本,但不实际执行,用于检查是否有语法错误 | |-x | 显示每一条命令的执行过程,是调试脚本的强有力工具 | |-c | 从字符串中读取命令并执行,用来临时测试一小段脚本 |

当脚本非常庞大时,-x 的输出会很多,这时可以使用 set -x命令

[root@zhujipeng test]# cat test.sh
foo() {
  echo haha
}

echo call foo
foo
echo

echo debug foo
set -x
foo
set +x
echo

echo call foo
foo
[root@zhujipeng test]# sh test.sh
call foo
haha

debug foo
+ foo
+ echo haha
haha
+ set +x

call foo
haha

脚本变量

对于复杂的shell脚本的调试来说,需要输出行号和函数等相关信息

不是所有的shell都支持这些变量

变量 说明
LINENO 当前行号,类似于C语言中的内置宏__LINE__
FUNCNAME 函数名称,类似于C语言中的内置宏__func__
PS4 调试时输出的前缀,缺省的$PS4的值是"+"号

主提示符变量$PS1和第二级提示符变量$PS2比较常见 FUNCNAME是一个数组变量,包含了调用链上所有的函数的名字 变量${FUNCNAME[0]}代表shell脚本当前正在执行的函数的名字

[root@zhujipeng test]# cat test.sh
PS4="[+$LINENO:${FUNCNAME[0]}]"
echo haha
[root@zhujipeng test]# sh -x test.sh
+ PS4='[+1:]'
[+1:]echo haha
haha



参考

Shell脚本调试技术
SIGTERM、SIGKILL、SIGINT和SIGQUIT的区别
我使用过的Linux命令之trap

Copyright © zhujipeng 2017 all right reserved,powered by Gitbook 该文件修订时间: 2017-11-04 14:57:24

results matching ""

    No results matching ""