895 字
4 分钟
写出健壮的 Bash 脚本:选项、陷阱与防御式写法

在终端里能跑的命令,写进脚本就出事,是新手写 Bash 的日常。大多数事故来自几个可预期的陷阱:变量没加引号、命令失败没被察觉、空变量拼出危险路径、定时任务环境和交互终端不一样。本文给出一套防御式写法骨架。

起手式:set -euo pipefail#

脚本开头建议固定写上:

#!/usr/bin/env bash
set -euo pipefail

三个选项各自的含义:

  • -e(errexit):任何命令返回非零退出码,脚本立即退出。没有它,备份脚本里「进入目录失败」之后,rm 照样会在错误的位置执行。
  • -u(nounset):使用未定义的变量直接报错。把 rm -rf "$bakup_dir/"(手滑拼错变量名,展开为空)这类事故扼杀在摇篮。
  • pipefail:管道中任一环节失败,整条管道算失败。grep error app.log | wc -l 在日志文件不存在时默认仍返回 0 并输出 0,开了 pipefail 才会暴露 grep 的报错。

注意 -e 有些反直觉的例外:if 条件里、&&、|| 左侧的命令失败不会触发退出,这是特意保留的判断能力。

变量:永远加引号#

单词分割是 Bash 事故第一大来源。文件名含空格时:

Terminal window
file="daily report.txt"
cat $file # 报错:被拆成 "daily" 和 "report.txt" 两个参数
cat "$file" # 正确:作为一个参数

规则只有一条:变量展开一律加双引号,除非你明确需要分词(很少见)。相关约定:

  • 命令替换用 $( ) 而不是反引号,可嵌套、可读性好。
  • 只读变量用 readonly BACKUP_DIR=/data/backup,脚本开头的配置项建议大写。
  • 拼路径时确认变量非空:[ -n "$dir" ] || { echo "dir 为空" >&2; exit 1; }。

条件与退出码#

[ ] 是命令不是语法糖,常见判断:

Terminal window
[ -f /etc/nginx/nginx.conf ] # 文件存在
[ -d /var/log ] # 目录存在
[ -z "$var" ] # 字符串为空
[ "$code" -eq 200 ] # 数值比较用 -eq/-ne/-gt/-lt
[ "$ans" = "y" ] # 字符串比较用 =

判断上一条命令成败,直接把它放进 if:

Terminal window
if systemctl is-active --quiet nginx; then
echo "nginx 运行中"
else
echo "nginx 已停止,尝试启动" >&2
sudo systemctl start nginx
fi

不要先执行命令再判断 $?,那会让 set -e 提前把脚本杀掉。

日志与错误输出#

脚本的输出分两类:结果是 stdout,诊断信息走 stderr(>&2)。这样 脚本 > result.txt 不会把日志混进数据。一个简单可用的日志函数:

Terminal window
log() { printf '[%s] %s\n' "$(date '+%F %T')" "$*" >&2; }

循环里处理文件列表,用 find -print0 配合 read -d '',避免文件名含空格、换行时出错:

Terminal window
find /data -name '*.tmp' -print0 | while IFS= read -r -d '' f; do
rm -- "$f"
done

定时任务环境差异#

「手动跑没问题,cron 里就失败」几乎总是环境差异:cron 的 PATH 很短、没有交互终端、工作目录不同。对策:

  1. 脚本里调用外部命令写绝对路径,或在开头显式 export PATH=/usr/local/bin:/usr/bin:/bin;
  2. 一律使用绝对路径引用文件;
  3. cron 条目里追加 >> /var/log/myjob.log 2>&1,留好现场;
  4. 需要交互输入的命令(如未配置免密的 ssh)不要放进 cron。

提交前的最后一步:shellcheck#

ShellCheck 是 Bash 静态检查工具,能抓出未加引号、弃用语法、误判退出码等绝大多数低级错误:

Terminal window
shellcheck backup.sh

把它当成 Bash 的编译器警告来对待,告警清零再上线。配合上面这套骨架,脚本的可靠性会提升一个量级。

延伸阅读#

写出健壮的 Bash 脚本:选项、陷阱与防御式写法
https://meow.us.kg/posts/bash-scripting-robust-practices/
作者
もゆちゃん
发布于
2026-09-06
许可协议
CC BY-NC-SA 4.0

分享文章

生成精美分享图或复制链接,与更多人分享本文。

继续阅读

沿着主题读

基于共同的标签与分类

换条路线

从其他文章中稳定抽取