trap 是 shell 脚本实现优雅退出和资源清理的核心机制,通过在脚本早期注册 trap 'cleanup' int term hup exit,确保收到信号或退出时执行快速、可重入的清理函数,并导出必要变量,临时屏蔽信号需成对恢复,且必须用真实信号验证效果。

Trap 是 Shell 脚本实现优雅退出和资源清理的核心机制。它不阻止信号,而是让脚本在收到 SIGINT、SIGHUP、SIGTERM 等信号时,先执行自定义清理逻辑,再退出,避免临时文件残留、子进程失控或状态不一致。
必须在开头注册,覆盖关键信号
trap 命令必须在脚本启动早期就设置,最好紧接在变量声明或参数解析之后——一旦信号到达,未注册的 trap 就完全失效。
- 推荐一次性绑定多个信号:trap 'cleanup' INT TERM HUP EXIT
- INT 对应 Ctrl+C,TERM 是 kill -15 的标准终止请求,HUP 常见于 SSH 断开或 systemd stop,EXIT 则兜底保障:无论成功、失败、被杀,只要脚本退出就会触发
- 不要遗漏 HUP:高并发脚本中它极易引发子进程残留,但常被忽略
清理函数要快、稳、可重入
cleanup 函数由 trap 触发时,会在新子 shell 中运行,因此不能依赖父 shell 的局部变量,也不能做耗时或不可控操作。
- 所有需使用的路径、PID 等变量,提前 export(例如 export TMPDIR PID)
- 清理动作务必快速确定:删文件用 [ -n "$TMPDIR" ] && rm -rf "$TMPDIR",关子进程用 kill -TERM "$PID" 2>/dev/null || :
- 避免阻塞调用:不要在 cleanup 中直接执行无超时的 curl、ssh 或 wait -n;如需上报,改用 timeout 3s curl -s ... || :
- 开头加 set -e,确保任一命令失败立即终止 cleanup,防止跳过关键步骤
临时屏蔽与恢复要成对出现
临界区(如原子写配置、切换符号链接)中需防中断,可用 trap '' 临时忽略信号,但必须立刻恢复,否则脚本将无法被正常管理。
- 屏蔽示例:trap '' INT —— 暂时不响应 Ctrl+C
- 恢复示例:trap - INT —— 恢复默认行为(即终止)
- 切勿长期屏蔽 TERM 或 HUP:systemd、supervisord 等工具依赖它们控制服务生命周期
- 用户自定义信号(如 SIGUSR1)适合做运行时开关,不影响标准运维流程
验证是否生效,得用真实信号测试
仅靠 exit 或 echo 测试 trap 是无效的。真正上线前,必须模拟生产环境中的中断方式。
- 后台启动:./script.sh &,记下 PID($!)
- 分别测试:kill -INT $!(模拟 Ctrl+C)、kill -TERM $!(systemd 停止)、kill -HUP $!(SSH 断开)
- 观察日志或临时文件是否被清除,子进程是否退出,确认 cleanup 全流程走通
- 注意:SIGKILL(kill -9)永远无法捕获,这是系统强制终止机制,无需也不应尝试处理











