系统与运维

Bash 脚本防坑实战:set -euo pipefail 与 trap 让脚本真正可信

多数 Bash 脚本「跑完了」不代表「跑对了」:命令失败不退出、变量为空不报错、管道掩盖上游崩溃。本文用一套可直接套用的骨架(set -euo pipefail + trap + 引号纪律)根治这些静默失败。

作者:巧匠团队·7 分钟阅读·更新于 2026-08-31

裸脚本的三种「静默失败」

Bash 默认的容错行为是排障地狱的根源:命令失败不会退出(-e 未开)、未定义变量当空字符串用(-u 未开)、管道只看最后一个命令的退出码(pipefail 未开)。

后果是脚本「看起来跑完了」,实际中途就错了:cd 失败还在原目录继续删文件、变量名打错静默变空、grep | head 掩盖上游崩溃。线上事故往往就藏在这几行里。

cd /data/release && rm -rf old/    # cd 失败时 rm 根本不该执行
cp app.conf /etc/app.conf            # 失败了脚本照样往下走
echo "deploy done"                    # 假象:一切都好

第一道防线:set -euo pipefail

shebang 之后立刻加上 set -euo pipefail:-e 任一命令非零即退出;-u 引用未定义变量直接报错而非当空串;-o pipefail 让管道以第一个失败命令的退出码为准。

注意 -e 的例外:条件上下文(if/while/&&/||)中的失败不会触发退出——这是特性不是 bug,cmd || exit 1、if grep -q ... 这类写法依然要显式处理。

#!/usr/bin/env bash
set -euo pipefail
# 调试期可加:逐条回显执行的命令
# set -x

清理现场:trap 保底

脚本中途失败或被 Ctrl+C 杀掉时,临时文件、后台子进程、锁文件都会残留。用 trap 挂在 EXIT 上统一清理:无论正常结束、报错退出还是被中断,清理函数都会执行。

临时目录用 mktemp -d 创建(不要拼固定路径,避免并发冲突与符号链接攻击),清理函数只删自己创建的目录。

tmpdir=$(mktemp -d)
cleanup() { rm -rf "$tmpdir"; }
trap cleanup EXIT
# 需要 Ctrl+C / kill 也走一遍显式清理时可再加:
# trap cleanup INT TERM

引号纪律:所有展开都加双引号

未加双引号的变量会在空格处拆词、空变量直接消失:rm -rf $dir 在 dir 为空时等于裸 rm -rf(灾难级)。纪律很简单——所有 $var 一律写成 "$var"。

三个高频配套写法:文件名可能以 - 开头时用 -- 分隔符或 ./ 前缀;必填参数用 ${1:?提示} 缺失即报错退出;可选值用 ${2:-默认值}。

rm -rf -- "$dir"                    # -- 防止文件名被当成选项
src="${1:?用法: $0 <源目录>}"           # 缺参数直接报错退出
out="${2:-./dist}"                    # 提供默认值

输入校验与幂等

外部输入(位置参数、环境变量、find 的结果)使用前先验证:依赖的命令用 command -v 检查,路径用 [ -d ]/[ -f ] 检查。宁可启动时报错,不要跑一半才崩。

让脚本可重复执行(幂等):mkdir -p 代替 mkdir,cp -f/rsync 代替裸 cp。半途失败的脚本可以直接重跑,不用人工清场。

command -v jq >/dev/null || { echo "需要 jq" >&2; exit 1; }
[ -d "$src" ] || { echo "目录不存在: $src" >&2; exit 1; }
mkdir -p "$out"                      # 幂等:已存在不报错

上生产前的调试与清单

三个工具覆盖九成问题:bash -n 只查语法,bash -x 逐行回显执行过程,shellcheck 静态分析能抓住上面几乎所有坑(包括未加引号的变量)。把 shellcheck 接进 CI,脚本质量问题基本绝迹。

最终清单:shebang + set -euo pipefail + trap 清理 + 全变量双引号 + 入参校验 + 幂等写法 + shellcheck 通过。照抄这套骨架,脚本就从「碰运气」变成「可信」。

bash -n deploy.sh        # 只查语法
bash -x deploy.sh        # 逐行回显执行(调试)
shellcheck deploy.sh     # 静态分析(推荐装进 CI)

官方参考来源

下方为命令对应的官方权威文档,供你核对最新用法与深入查阅。