11.2.3 Shell脚本的排错
Shell脚本在执行时出错是很常见的,最简单的原因无外乎脚本在编写的过程中出现了语法错误或者不小心输错了命令等。找出脚本中的错误是很重要的能力。假设在之前的脚本中,不小心把echo命令写成了ehco,那么执行的效果如下:
- [root@localhost ~]# bash HelloWorld.sh
- HelloWorld.sh: line 2: ehco: command not found
从报错信息很容易了解到,出错的原因是“命令不存在”。重新编辑这个文件修改成echo就可以了。如果只是语法上的错误或命令错误还是比较容易辨别的,但往往一些逻辑或算法错误不容易发现,因为它的语法正确且本身并不会造成程序运行出错。比如说下面的脚本,本来是想连续10次做某些操作的,结果却迟迟没有任何输出。仔细观察一下就知道,其实是陷入了死循环。
- [root@localhost ~]# cat BadLoop.sh
- #!binbash
- for ((i=10;i>0;i=i+1))
- do
- #do something here
- done
如果在循环中间的代码块中加入了echo语句,那就容易发现问题了。也就是说,在排查错误时,使用echo命令有助于观察代码执行的情况。假设在上面的脚本中使用了echo,再次运行脚本时会注意到脚本在不断地打印输出,这样就可以发现异常了。如果遇到确实需要执行很多次循环的代码段,由于每次循环的过程都很快,可能来不及观察就进入了下一次循环,那么可以加入sleep命令降低单次循环的速度,比如使用sleep 2这样的命令,单次循环至少会耗时两秒,这就有足够的时间观察循环的过程了。
- #!binbash
- for ((i=10;i>0;i=i+1))
- do
- #do something here
- echo "i=$i";
- sleep 2
- done
为了更清晰地看到脚本运行的过程,还可以借助-x参数来观察脚本的运行情况。比如在下面的代码段中,从输出可以看到变量i的值一直在向上增加,永远满足x>0的条件,所以这是一个死循环。只需要将原来代码中的i=i+1改成i=i-1就可以了。
- [root@localhost ~]# bash -x BadLoop.sh
- + (( i=10 ))
- + (( i>0 ))
- + echo i=10
- i=10
- + sleep 2
- + (( i=i+1 ))
- + (( i>0 ))
- + echo i=11
- i=11
- + sleep 2
- + (( i=i+1 ))
- + (( i>0 ))
- + echo i=12
- i=12
- + sleep 2
- [Ctrl+c]
- 停止脚本
Shell本身并没有提供更好的排错工具,想要尽量减少错误,除了多学习它的语法,多写、多想、多改之外并没有更好的办法,只有勤加练习才能更快地进步。
为了更精细地调试运行Shell,我们可以借助第三方工具bashdb。这是一个类似于GDB的脚本调试软件,小巧而强大,具有设置断点、单步执行、观察变量等功能。读者可以到这个页面下载使用:http://sourceforge.jp/projects/sfnet_bashdb,请注意最新的版本(笔者在写本书时为4.2.-0.8,支持的Shell必须是4.2,如果你使用的是3.2版本的Bash,则请使用3.1版本的bashdb)。
具体下载安装的过程如下:
- #
- 第一步:下载bashdb
- (笔者使用的版本是3.1
- )
- [root@localhost ~]# wget \
- http://nchc.dl.sourceforge.net/project/bashdb/bashdb/3.1-0.09/bashdb-3.1-0.09.tar.gz
- --2013-10-10 07:15:13--
- http://nchc.dl.sourceforge.net/project/bashdb/bashdb/3.1-0.09/bashdb-3.1-0.09.tar.gz
- Resolving nchc.dl.sourceforge.net... 211.79.60.17, 2001:e10:ffff:1f02::17
- Connecting to nchc.dl.sourceforge.net|211.79.60.17|:80... connected.
- HTTP request sent, awaiting response... 200 OK
- Length: 659032 (644K) [application/x-gzip]
- Saving to: `bashdb-3.1-0.09.tar.gz'
- 100%[======================================>] 659,032 71.1K/s in 9.2s
- 2013-10-10 07:15:22 (69.9 KB/s) - `bashdb-3.1-0.09.tar.gz' saved [659032/659032]
- #
- 第二步:解压并进入解压后的目录
- [root@localhost ]# tar zxvf bashdb-3.1-0.09.tar.gz
- [root@localhost ]# cd bashdb-3.1-0.09
- #
- 第三步:配置
- [root@localhost bashdb-3.1-0.09]# ./configure
- checking for emacs... emacs
- checking where .elc files should go...
- ${datarootdir}/emacs/site-lisp
- checking whether to enable maintainer-specific portions of Makefiles... no
- checking for a BSD-compatible install... usrbin/install -c
- ......(
- 略去内容)......
- config.status: executing default commands
- =========================================================
- Bash version: GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
- Copyright (C) 2005 Free Software Foundation, Inc.
- Location: binbash
- We will not try to build the readarray builtin to speed up loading.
- #
- 第四步:编译并安装
- [root@localhost bashdb-3.1-0.09]# make && make install
- make all-recursive
- make[1]: Entering directory `/root/bashdb-3.1-0.09'
- Making all in test
- make[2]: Entering directory `/root/bashdb-3.1-0.09/test'
- make[2]: Nothing to be done for `all'.
- make[2]: Leaving directory `/root/bashdb-3.1-0.09/test'
- Making all in doc
- ......(
- 略去内容)......
- make[4]: Leaving directory `/root/bashdb-3.1-0.09'
- test -d usrlocal/share/bashdb || binmkdir -p usrlocal/share/bashdb
- make[3]: Leaving directory `/root/bashdb-3.1-0.09'
- make[2]: Leaving directory `/root/bashdb-3.1-0.09'
- make[1]: Leaving directory `/root/bashdb-3.1-0.09'
一旦安装完成,系统中便有了bashdb命令,该命令典型的用法如下:
- [root@localhost ~]# bashdb --debug
- 脚本名
更多命令可以在调试模式中使用,按照功能可划分为查看源代码相关功能、调试相关功能、控制相关功能三大部分,如表11-1所示,也可以使用help命令调出帮助信息。
表11-1 bashdb的命令列表