写出更健壮的 Bash 脚本:set -euo pipefail 与几个好习惯
很多运维小脚本一开始只有几行,能跑就行。可一旦放进定时任务或部署流程,问题就来了:中间某条命令失败了,脚本却继续往下执行,最后把错误的结果当成成功;变量拼错了名字,rm 命令删掉了意料之外的目录。其实 Bash 本身提供了几个开关和写法,能让脚本在出错时及时停下来。本文整理几条最实用的习惯。
一、开头加上 set -euo pipefail
在脚本第二行写上 set -euo pipefail,可以一次打开三个保护:
-e:任何命令返回非零就立即退出,不再继续执行后面的步骤。-u:引用未定义的变量时直接报错,避免因为拼写错误得到空字符串。-o pipefail:管道中任意一段失败,整个管道就算失败。默认情况下只看最后一段,像curl ... | grep ok这样的写法,curl 失败了也可能被忽略。
需要允许某条命令失败时,可以显式写成 command || true,让意图一目了然。
二、变量一律加双引号
写 rm -rf $DIR/tmp 时,如果 DIR 为空,实际执行的就是 rm -rf /tmp;如果路径里有空格,又会被拆成多个参数。养成写 "$DIR" 的习惯,再配合 -u,或者用 ${DIR:?DIR 未设置} 在变量为空时直接报错退出,能挡住很多事故。
三、用 trap 做清理
脚本经常会创建临时文件。如果中途出错退出,这些文件就留在磁盘上。可以这样写:
tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT
无论脚本是正常结束还是因为错误退出,EXIT 陷阱都会执行,临时文件总能被清理掉。同样的方法也可以用来释放锁文件。
四、输出带时间的日志
定时任务出问题时,最缺的就是日志。写一个简单的函数:
log() { echo "[$(date '+%F %T')] $*" >&2; }
关键步骤前后调用一次,并把输出重定向到日志文件,事后排查就能看出卡在了哪一步、用了多久。把日志写到标准错误,也不会污染需要被其他程序读取的标准输出。
五、交给 ShellCheck 把关
ShellCheck 是一个静态检查工具,能发现未加引号的变量、错误的条件判断、不可移植的语法等问题。大部分发行版可以直接用包管理器安装,执行 shellcheck deploy.sh 就能看到逐行提示。把它加进代码提交前的检查,团队里的脚本质量会明显提高。
小结
Bash 脚本写起来快,但默认行为相当“宽容”,很多错误会被悄悄吞掉。开头的 set -euo pipefail、变量加引号、trap 清理、带时间戳的日志,再加上 ShellCheck 检查,这几步花不了几分钟,却能让脚本在云服务器上长期稳定地运行。
- 点赞
- 收藏
- 关注作者
评论(0)