Shell Daily 2025-12-21: 管道防隐患 (Pipefail)
Shell 管道 (|) 有个极其危险的默认行为:整个管道的退出状态只取决于最后一个命令。这意味着,前面的关键任务如果崩了,只要最后一步(比如格式化输出)成功了,脚本就会欺骗你说“一切正常”。
怎么用
在脚本开头加上这行配置,开启“严格模式”。
- Bash / Zsh:
set -o pipefail - POSIX sh: 不支持(这是 ksh/Bash 的扩展特性)。
适用场景
任何涉及数据流转的自动化任务,特别是数据库备份、CI/CD 构建流水线或日志处理。
示例 1:数据库备份的隐患
这是最典型的翻车现场。如果数据库密码错误,导出失败,但压缩命令是成功的,你会得到一个 0 字节的压缩包,且脚本显示“成功”。
# 默认模式:mysqldump 失败,但 gzip 成功,最终 $? 为 0
mysqldump -u root db_name | gzip > backup.sql.gz# 开启保护
set -o pipefail
# 此时只要 mysqldump 报错,整个管道就会返回非零状态
# 结合 set -e 使用,脚本会立即停止,避免后续误操作
mysqldump -u root db_name | gzip > backup.sql.gz
示例 2:不丢失的构建状态
我们常把构建日志同时输出到文件和屏幕。如果不加 pipefail,编译失败了 CI 却可能显示 Pass。
set -o pipefail# 如果 ./build_script.sh 失败
# 即使 tee 命令成功写入了日志,整行命令也会返回失败码
./build_script.sh | tee build.log