set -e 并不总是生效,它仅对简单命令的非零退出码敏感,而对 if、&&/||、管道中间命令、子 shell 等场景静默忽略;需配合 pipefail、手动检查 $?、trap 清理等机制才能实现真正健壮的错误处理。

shell 脚本里 set -e 并不总是生效
很多人以为加了 set -e 就能自动退出所有错误,其实它对很多常见场景完全静默:if 判断、&&/|| 链式操作、管道中的中间命令,甚至子 shell 里的失败都不会触发退出。
-
set -e只对“简单命令”(simple command)的非零退出码敏感,而command1 | command2中的command1失败不会让整个 pipeline 返回非零,除非加set -o pipefail -
if false; then echo ok; fi不会因false退出——这是语法结构本身的设计,set -e主动让渡控制权 - 用
( some_command )启动子 shell 时,其内部失败也不会传播到父脚本,除非显式检查$?
真正可靠的错误捕获:必须手动检查 $? 或用 || 显式处理
依赖 set -e 容易产生漏网之鱼。健壮脚本应把关键步骤的失败当作“需要决策的事件”,而不是靠全局开关甩手不管。
- 对可能失败的关键命令(如
cp、curl、mysql),紧接其后用|| { echo "cp failed"; exit 1; } - 用
if ! some_command; then ... fi比some_command || ...更清晰,尤其当错误处理逻辑不止一行时 - 调用外部脚本后,立刻检查
$?:如果被调脚本返回 100 表示重试,返回 1 表示致命错误,你得区分对待,不能只看是否为 0
管道错误必须配 set -o pipefail,否则 grep 找不到内容就等于“成功”
默认情况下,cmd1 | cmd2 | cmd3 的退出状态只取 cmd3 的值。哪怕 cmd1 已经因为权限问题失败,只要 cmd3(比如 head -1)顺利执行完,整个管道就返回 0。
PHP5学习对象教程由美国人古曼兹、贝肯、瑞桑斯编著,简张桂翻译,电子工业出版社于2007年12月1日出版的关于PHP5应用程序的技术类图书。该书全面介绍了PHP 5中的新功能、编程方法及设计模式,还分析阐述了PHP 5中新的数据库连接处理、错误处理和XML处理等机制,帮助读者系统了解、熟练掌握和高效应用PHP。
set -o pipefail
if ! output=$(find /root -name "*.log" 2>/dev/null | grep -E '\.log$' | head -1); then
echo "no log file found or permission denied"
exit 1
fi
- 没有
set -o pipefail,上面find /root的Permission denied错误会被吞掉,$output为空但脚本继续运行 -
pipefail让管道整体返回第一个失败命令的退出码,这才是符合直觉的行为 - 注意:某些旧版 dash/sh 不支持
pipefail,生产环境若需兼容 POSIX,只能拆成临时文件或分步检查
信号中断和清理逻辑要用 trap,别指望 set -e 能兜底
Ctrl+C(SIGINT)、超时(SIGTERM)或磁盘满导致的 SIGPIPE,set -e 完全无感。这些场景下资源(文件句柄、临时目录、锁文件)不释放,就会留下故障残留。
- 用
trap 'rm -f "$tmpfile"; rmdir "$tmpdir" 2>/dev/null' EXIT INT TERM确保退出前清理 -
EXITtrap 在所有退出路径(包括exit 0、exit 1、自然结束)都会触发;INT/TERM则捕获对应信号 - 避免在
trap里调用复杂函数或依赖未初始化的变量——trap执行时上下文可能已部分销毁
健壮性不是靠一个开关撑起来的,而是每处 IO、每次子进程、每个信号点都得想好“出事了怎么办”。最危险的,是以为加了 set -e 就万事大吉,结果上线后某个 grep 在空输入上静默失败,后续逻辑全跑偏。









