最直接有效的方式是用 trap 捕获中断信号并在脚本退出前自动执行清理;需在开头注册 trap cleanup INT TERM HUP EXIT,确保临时文件、锁、子进程等被安全释放,且 cleanup 函数要快、稳、可重入并避免阻塞。

用 trap 捕获中断信号,在脚本退出前自动执行清理,是最直接有效的方式。关键不是“阻止终止”,而是确保无论怎么退出(Ctrl+C、kill、SSH断开、出错退出),临时文件都能被删掉。
必须在脚本开头就注册 trap
trap 命令要放在变量定义、参数解析之后,但必须早于任何可能创建临时资源的操作——否则信号来得早,cleanup 就根本不会运行。
- 推荐一次性绑定多个退出触发点:
trap cleanup INT TERM HUP EXIT - INT 对应 Ctrl+C;TERM 是 kill -15 的标准停止信号;HUP 常见于 SSH 断开或 systemd 重启;EXIT 是兜底项,脚本结束必触发(包括 exit 0 或 exit 1)
- 别漏掉 HUP:它不显眼,但在远程执行或服务化场景中极易导致锁未释放、子进程残留
清理函数要快、稳、可重入
trap 调用的函数会在新子 shell 中执行,不能依赖局部变量,也不能卡住。
- 所有要用的路径、PID 等,提前
export(例如export TMPDIR="/tmp/myapp.$$") - 删除操作加判断:
[ -n "$TMPDIR" ] && rm -rf "$TMPDIR" - 关子进程用
kill -TERM "$PID" 2>/dev/null || :,避免因进程已死报错中断 cleanup - 禁用阻塞调用:不要在 cleanup 里直接
wait或无超时curl;如需上报,加timeout 3s curl ... || :
避免 trap 被覆盖的坑
同一个信号只能有一个 trap 生效。后写的会完全替换前面的。
- 错误写法:
trap "rm -f /tmp/a" EXIT,再写trap "rm -f /tmp/b" EXIT→ 只有 b 被删 - 正确做法:统一进一个函数,集中管理所有清理项
- 示例:
cleanup() {<br> [ -n "$TMPDIR" ] && rm -rf "$TMPDIR"<br> [ -f "$LOCKFILE" ] && rm -f "$LOCKFILE"<br> echo "Cleanup done." >&2<br> }<br> trap cleanup EXIT INT TERM HUP
临界区临时屏蔽信号要成对出现
比如原子写配置、切换软链接时,不希望被 Ctrl+C 中断,可以短暂忽略 INT,但必须立刻恢复。
- 屏蔽:
trap '' INT - 恢复:
trap - INT(-表示恢复默认行为) - 切勿长期屏蔽 TERM 或 HUP:否则 systemd、supervisord 等无法正常 stop 你的服务
- 用户自定义信号(如 SIGUSR1)更适合做运行时开关,不影响运维流程

















