为什么失败会静默
退出码为 0 只表示进程正常结束,不表示业务达成。静默失败有几种常见来源:管道里前段命令失败但脚本取的是最后一段的退出码;子 shell 中的错误不向外传播;显式忽略标记把失败吞掉;命令不存在时返回的码被判读为普通错误。这些情况都会让自动化报告「成功」,而问题在更晚的阶段以更贵的方式暴露。
基本信息
| 典型成因 | 管道掩盖退出码、子进程错误不外传、错误被忽略标记吞掉 |
|---|---|
| 检测手段 | 显式检查关键步骤返回码、校验产物、比对预期结果 |
| 脚本规范 | 启用错误中断与管道错误传播,慎用忽略标记 |
| 判读要求 | 区分命令不存在与命令执行失败两种情况 |
| 兜底措施 | 关键动作后追加校验步骤,不依赖单一返回码 |
典型使用场景
- 部署脚本报告成功但服务未生效:命令执行了,配置未加载
- 备份任务成功但产物为空:写入路径或权限问题被忽略
- 权限设置失败但后续步骤继续:最终结果看起来完成
- 定时任务未执行却无告警:没有输出被误认为没有异常
- 数据同步部分失败:单条失败被忽略,整体标记为完成
成因、表现与规避
| 成因 | 表现 | 规避方式 |
|---|---|---|
| 管道掩盖退出码 | 最后一段成功即判为成功 | 启用管道错误传播选项 |
| 命令不存在 | 返回非预期值却被当作普通失败忽略 | 执行前校验命令可用性 |
| 错误被忽略标记 | 失败被吞,流程继续向后执行 | 仅对可容忍错误使用忽略 |
| 产物未校验 | 返回成功但文件为空或损坏 | 校验大小、可读性与内容特征 |
| 子进程错误不外传 | 父脚本判定成功 | 显式检查子调用结果并传播 |
| 定时任务无输出 | 未执行也无人察觉 | 上报最近一次执行时间与结果 |
让失败不再静默
让失败不再静默
#!/bin/bash
set -euo pipefail # 遇错即停、未定义变量报错、管道任一环失败即失败
command -v rsync >/dev/null || { echo 'rsync missing'; exit 3; } # 先确认命令存在
rsync -a /data/ /backup/ || { echo 'sync failed'; exit 4; } # 显式处理失败
[ -s /backup/index.db ] || { echo 'artifact empty'; exit 5; } # 校验产物非空
# 与预期数量比对,避免部分失败被整体判为成功
src=$(find /data -type f | wc -l); dst=$(find /backup -type f | wc -l)
[ "$src" -eq "$dst" ] || { echo "count mismatch $src/$dst"; exit 6; }退出码 0 只能证明进程正常结束。要判断业务是否达成,必须有一个独立的校验动作:产物存在且非空、服务处于监听、版本与预期一致。
只按退出码把自动化结果写入审计与报表,会把失败记录成成功。这类失真比失败本身更难处理,因为后续所有统计与决策都建立在错误前提上。