shell脚本在运行异常时会受到非常大的影响。
本文介绍一些让bash脚本变得健壮的技术。
使用set -u
因为没有对变量初始化而使脚本崩溃过多少次?对于我来说,很多次。
chroot=$1
...
rm -rf $chroot/usr/share/doc
如果上面的代码没有给参数就运行,不会仅仅删除掉chroot中的文档,而是将系统的所有文档都删除。那应该做些什么呢?好在bash提供了set -u,当使用未初始化的变量时,让bash自动退出。
也可以使用可读性更强一点的set -o nounset。
代码如下:
david% bash /tmp/shrink-chroot.sh
$chroot=
david% bash -u /tmp/shrink-chroot.sh
/tmp/shrink-chroot.sh: line 3: $1: unbound variable
david%
使用set -e
写的每一个脚本的开始都应该包含set -e。这告诉bash一但有任何一个语句返回非真的值,则退出bash。使用-e的好处是避免错误滚雪球般的变成严重错误,能尽早的捕获错误。更加可读的版本:set -o errexit
使用-e把从检查错误中解放出来。如果忘记了检查,bash会替做这件事。不过也没有办法使用$?来获取命令执行状态了,因为bash无法获得任何非0的返回值。可以使用另一种结构:
command
if [ "$?"-ne 0]; then echo "command failed"; exit 1; fi
可以替换成:
command || { echo "command failed"; exit 1; }
或者使用:
if ! command; then echo "command failed"; exit 1; fi
如果必须使用返回非0值的命令,或者对返回值并不感兴趣呢?可以使用 command || true ,或者有一段很长的代码,可以暂时关闭错误检查功能,不过我建议谨慎使用。
set +e
command1
command2
set -e
相关文档指出,bash默认返回管道中最后一个命令的值,也许是不想要的那个。比如执行 false | true 将会被认为命令成功执行。如果想让这样的命令被认为是执行失败,可以使用 set -o pipefail
程序防御 - 考虑意料之外的事
的脚本也许会被放到“意外”的账户下运行,像缺少文件或者目录没有被创建等情况。可以做一些预防这些错误事情。比如,当创建一个目录后,如果父目录不存在,mkdir 命令会返回一个错误。如果创建目录时给mkdir命令加上-p选项,它会在创建需要的目录前,把需要的父目录创建出来。另一个例子是rm 命令。如果要删除一个不存在的文件,它会“吐槽”并且的脚本会停止工作。(因为使用了-e选项,对吧?)可以使用-f选项来解决这个问题,在文件不存在的时候让脚本继续工作。
准备好处理文件名中的空格
有些人从在文件名或者命令行参数中使用空格,需要在编写脚本时时刻记得这件事。需要时刻记得用引号包围变量。
if [ $filename = "foo" ];
当$filename变量包含空格时就会挂掉。可以这样解决:
新闻热点
疑难解答