有时候想查看Shell Scripts到底哪里出了问题,不知道怎么办?
我需要的是像GDB那样的,一步一步执行,能查看变量的调试器。
[问题] 有没有Shell的调试器?
-
- 帖子: 5
- 注册时间: 2007-10-22 11:45
- BigSnake.NET
- 帖子: 12522
- 注册时间: 2006-07-02 11:16
- 来自: 廣州
- 联系:
- bones7456
- 帖子: 8495
- 注册时间: 2006-04-12 20:05
- 来自: 杭州
- 联系:
- eexpress
- 帖子: 58428
- 注册时间: 2005-08-14 21:55
- 来自: 长沙
- iblicf
- 帖子: 3766
- 注册时间: 2007-01-15 17:15
bs lsls 做广告 , cheat 行为
第29章. 调试
Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.
Brian Kernighan
Bash shell 没有自带调试器, 甚至没有任何调试类型的命令或结构. [1] 脚本里的语法错误或拼写错误会产生含糊的错误信息,通常这些在调试非功能性的脚本时没什么帮助.
例子 29-1. 一个错误的脚本
1 #!/bin/bash
2 # ex74.sh
3
4 # 这是一个错误的脚本.
5 # 哪里有错?
6
7 a=37
8
9 if [$a -gt 27 ]
10 then
11 echo $a
12 fi
13
14 exit 0
脚本的输出:
./ex74.sh: [37: command not found
上面的脚本有什么错误(线索: 注意if的后面)?
例子 29-2. 丢失关键字(keyword)
1 #!/bin/bash
2 # missing-keyword.sh: 会产生什么样的错误信息?
3
4 for a in 1 2 3
5 do
6 echo "$a"
7 # done # 第7行的必需的关键字 'done' 被注释掉了.
8
9 exit 0
脚本的输出:
missing-keyword.sh: line 10: syntax error: unexpected end of file
注意错误信息中说明的错误行不必一定要参考, 但那行是Bash解释器最终认识到是个错误的地方.
出错信息可能在报告语法错误的行号时会忽略脚本的注释行.
如果脚本可以执行,但不是你所期望的那样工作怎么办? 这大多是由于常见的逻辑错误产生的.
例子 29-3. test24, 另一个错误脚本
1 #!/bin/bash
2
3 # 这个脚本目的是为了删除当前目录下的所有文件,包括文件名含有空格的文件。
4 #
5 # 但不能工作.
6 # 为什么?
7
8
9 badname=`ls | grep ' '`
10
11 # 试试这个:
12 # echo "$badname"
13
14 rm "$badname"
15
16 exit 0
为了找出 例子 29-3 的错误可以把echo "$badname" 行的注释去掉. echo 出来的信息对你判断是否脚本以你希望的方式运行时很有帮助.
在这个实际的例子里, rm "$badname" 不会达到想要的结果,因为$badname 没有引用起来. 加上引号以保证rm 命令只有一个参数(这就只能匹配一个文件名). 一个不完善的解决办法是删除A partial fix is to remove to quotes from $badname and to reset $IFS to contain only a newline, IFS=$'\n'. 不过, 存在更简单的办法.
1 # 修正删除包含空格文件名时出错的办法.
2 rm *\ *
3 rm *" "*
4 rm *' '*
5 # Thank you. S.C.
总结该脚本的症状,
1. 终止于一个"syntax error"(语法错误)的信息, 或
2. 它能运行, 但不是按期望的那样运行(逻辑错误).
3.
它能运行,运行的和期望的一样, 但有讨厌的副作用 (逻辑炸弹).
用来调试不能工作的脚本的工具包括
1.
echo 语句可用在脚本中的有疑问的点上以跟踪了解变量的值, 并且也可以了解后续脚本的动作.
Tip
最好只在调试时才使用echo语句.
1 ### debecho (debug-echo), by Stefano Falsetto ###
2 ### 只有变量 DEBUG 设置了值时才会打印传递进来的变量值. ###
3 debecho () {
4 if [ ! -z "$DEBUG" ]; then
5 echo "$1" >&2
6 # ^^^ 打印到标准出错
7 fi
8 }
9
10 DEBUG=on
11 Whatever=whatnot
12 debecho $Whatever # whatnot
13
14 DEBUG=
15 Whatever=notwhat
16 debecho $Whatever # (这儿就不会打印了.)
2.
使用 tee 过滤器来检查临界点的进程或数据流.
3.
设置选项 -n -v -x
sh -n scriptname 不会实际运行脚本,而只是检查脚本的语法错误. 这等同于把 set -n 或 set -o noexec 插入脚本中. 注意还是有一些语法错误不能被这种检查找出来.
sh -v scriptname 在实际执行一个命令前打印出这个命令. 这也等同于在脚本里设置 set -v 或 set -o verbose.
选项 -n 和 -v 可以一块使用. sh -nv scriptname 会打印详细的语法检查.
sh -x scriptname 打印每个命令的执行结果, 但只用在某些小的方面. 它等同于脚本中插入 set -x 或 set -o xtrace.
把 set -u 或 set -o nounset 插入到脚本里并运行它, 就会在每个试图使用没有申明过的变量的地方打印出一个错误信息.
4.
使用一个"assert"(断言) 函数在脚本的临界点上测试变量或条件. (这是从C语言中借用来的.)
例子 29-4 用"assert"测试条件
1 #!/bin/bash
2 # assert.sh
3
4 assert () # 如果条件测试失败,
5 { #+ 则打印错误信息并退出脚本.
6 E_PARAM_ERR=98
7 E_ASSERT_FAILED=99
8
9
10 if [ -z "$2" ] # 没有传递足够的参数.
11 then
12 return $E_PARAM_ERR # 什么也不做就返回.
13 fi
14
15 lineno=$2
16
17 if [ ! $1 ]
18 then
19 echo "Assertion failed: \"$1\""
20 echo "File \"$0\", line $lineno"
21 exit $E_ASSERT_FAILED
22 # else
23 # return
24 # 返回并继续执行脚本后面的代码.
25 fi
26 }
27
28
29 a=5
30 b=4
31 condition="$a -lt $b" # 会错误信息并从脚本退出.
32 # 把这个“条件”放在某个地方,
33 #+ 然后看看有什么现象.
34
35 assert "$condition" $LINENO
36 # 脚本以下的代码只有当"assert"成功时才会继续执行.
37
38
39 # 其他的命令.
40 # ...
41 echo "This statement echoes only if the \"assert\" does not fail."
42 # ...
43 # 余下的其他命令.
44
45 exit 0
5.
用变量$LINENO和内建的caller.
6.
捕捉exit.
脚本中的The exit 命令会触发信号0,终结进程,即脚本本身. [2] 这常用来捕捉exit命令做某事, 如强制打印变量值. trap 命令必须是脚本中第一个命令.
捕捉信号
trap
当收到一个信号时指定一个处理动作; 这在调试时也很有用.
Note
信号是发往一个进程的非常简单的信息, 要么是由内核发出要么是由另一个进程, 以告诉接收进程采取一些指定的动作 (一般是中止). 例如, 按Control-C, 发送一个用户中断( 即 INT 信号)到运行中的进程.
1 trap '' 2
2 # 忽略信号 2 (Control-C), 没有指定处理动作.
3
4 trap 'echo "Control-C disabled."' 2
5 # 当按 Control-C 时显示一行信息.
例子 29-5. 捕捉 exit
1 #!/bin/bash
2 # 用trap捕捉变量值.
3
4 trap 'echo Variable Listing --- a = $a b = $b' EXIT
5 # EXIT 是脚本中exit命令产生的信号的信号名.
6 #
7 # 由"trap"指定的命令不会被马上执行,只有当发送了一个适应的信号时才会执行。
8 #
9
10 echo "This prints before the \"trap\" --"
11 echo "even though the script sees the \"trap\" first."
12 echo
13
14 a=39
15
16 b=36
17
18 exit 0
19 # 注意到注释掉上面一行的'exit'命令也没有什么不同,
20 #+ 这是因为执行完所有的命令脚本都会退出.
- windwiny
- 帖子: 2254
- 注册时间: 2007-03-13 17:26
-
- 帖子: 20
- 注册时间: 2008-01-10 0:02