Linux中用trap捕获EXIT信号(即0号伪信号)可实现脚本任何方式退出时的兜底清理,需在开头注册trap 'cleanup' EXIT,并配合INT/TERM/HUP等信号增强健壮性;cleanup函数须独立、快速、可重入,注意变量导出与安全操作。

Linux 中用 trap 捕获退出信号,核心是注册 EXIT(即信号 0)——它不是操作系统真实信号,而是 shell 提供的“脚本退出钩子”,能在脚本**任何方式结束时**(正常 exit、命令失败、被 kill、语法错误退出等)触发清理逻辑。
必须用 EXIT 实现兜底清理
EXIT 是保障资源不残留的关键。它和 INT、TERM 等不同:后者只在收到对应信号时触发,而 EXIT 覆盖所有退出路径,包括 exit 0、exit 1、自然运行到末尾,甚至部分异常终止场景(配合 set -e 效果更稳)。
- 写法统一:
trap 'cleanup' EXIT或trap cleanup EXIT(函数名不加引号也可,但带空格或重定向时建议加单引号) - 位置关键:必须放在脚本开头、变量定义之后、任何可能提前退出的逻辑之前
- 不要只靠它:
EXIT不能替代INT/TERM/HUP,因为某些情况下(如语法错误、set -u未定义变量)EXIT可能不执行;所以推荐组合注册:trap 'cleanup' INT TERM HUP EXIT
清理函数要独立、快速、可重入
当 EXIT 触发时,cleanup 是在新子 shell 中运行的,无法访问父 shell 的局部变量,也不能阻塞或依赖未导出的状态。
- 提前导出关键变量:比如
export TMPDIR PID LOGFILE,确保cleanup内能读取 - 检查再操作:
[ -n "$TMPDIR" ] && rm -rf "$TMPDIR",避免rm -rf ""误删根目录 - 安全终止子进程:
kill -TERM "$PID" 2>/dev/null || :,加|| :防止 cleanup 因 kill 失败而中断 - 禁用耗时调用:不在
cleanup中直接curl、ssh或wait,如需上报,改用timeout 2s curl -s ... || :
注意 EXIT 不总是 100% 可靠
某些极端情况会导致 EXIT trap 完全不运行,属于 shell 行为限制,需提前规避:
- 脚本本身存在语法错误(如未闭合引号、错位
fi),shell 解析失败,根本不会进入执行阶段 - 使用
exec替换当前 shell 进程后,原 trap 全部失效 - 收到
SIGKILL(kill -9)时,内核强制终止,任何 trap 都无法捕获 - 在子 shell 中(如
( ... )或管道右侧)设置的 trap,不会影响父脚本的 EXIT 行为
验证是否真正生效
别只靠 exit 命令测试。真实验证要模拟生产中断场景:
- 后台启动脚本:
./myscript.sh & echo $!记下 PID - 分别发送信号:
kill -INT $!(模拟 Ctrl+C)、kill -TERM $!(systemd stop)、kill -HUP $!(SSH 断开) - 观察:临时文件是否消失?子进程是否退出?日志里是否有 cleanup 输出?
- 补测正常退出:
kill -TERM $!后再echo $?确认退出码符合预期


















