只加 set -e 不可靠,因其对管道(|)、条件语句、||/&& 等场景默认不生效;必须配合 set -o pipefail(使管道任一命令失败即整体失败)和 set -u(防止未定义变量静默展开)才真正健壮。

加 set -e 就行,但光加这一个不够,必须配合 set -o pipefail 和 set -u 才算真正可靠。
为什么只写 set -e 会失效?
因为 set -e 对管道命令(|)默认不敏感:只要最后一个命令返回 0,整条管道就算“成功”,脚本继续往下跑。比如:
ps -ef | grep "myapp" | awk '{print $2}'
如果 ps -ef 失败(权限不足或进程表不可读),但 grep 或 awk 没报错,整个管道退出码仍是 0 —— set -e 完全不拦。
-
set -o pipefail才能让管道中任意一环失败,整体就失败 - 不加它,
set -e在涉及|、&&、||的地方基本形同虚设 - 很多线上故障就出在这里:日志里看似“执行完成”,其实中间一步早挂了
set -e 不生效的常见场景
这些地方即使命令失败,set -e 也不会终止脚本:
- 出现在
if、while、until条件判断中的命令,例如if ! ls /missing; then ... fi - 用
||显式处理失败的命令,例如rm file.txt || echo "file not exist" - 子 shell 中执行:
(ls /missing)失败不影响父脚本 - 整行命令包含多个子命令且最终返回 0,例如
cmd1 || cmd2中cmd1失败但cmd2成功 → 整行返回 0
这不是 bug,是设计:它允许你自然写容错逻辑,而不是被迫套一堆 if。
推荐开头写法及临时关闭方式
标准健壮性组合是:
#!/bin/bash set -euo pipefail
其中 -u 防止未定义变量静默展开为空(比如把 $USER_NAME 拼成 $USERNMAE 还照常运行)。
真要容忍某条命令失败,别用 || true,更清晰的是:
- 用
set +e临时关,执行完立刻set -e恢复 - 或者直接包进
if:例如if ! ping -c1 host &>/dev/null; then echo "host down"; fi - 切忌在函数里
set -e后不恢复——它会影响调用方行为
最易被忽略的是:子 shell、管道、条件语句里的命令失败都不会触发退出,而生产环境里这些恰恰最常用。靠 set -e 单打独斗,等于没设。











