信号捕获是在Shell脚本中用trap监听SIGINT、SIGHUP等信号并执行自定义清理逻辑以实现优雅退出,关键包括提前定义trap、编写含子进程终止/临时文件清理/终端恢复/日志记录的cleanup函数,并避开子shell不继承、EXIT trap失效及SIGKILL不可捕获等陷阱。

什么是信号捕获
信号捕获是指在 Shell 脚本中监听特定系统信号(如 SIGINT、SIGHUP、SIGTERM),并在接收到时执行自定义清理逻辑,而不是直接终止。这是实现“优雅退出”的基础——比如关闭临时文件、释放端口、保存状态或通知子进程退出。
用 trap 捕获信号
trap 是 Shell 中处理信号的核心命令。基本语法是:
trap 'command' SIGNAL
常见写法:
- trap 'echo "收到 Ctrl+C,正在退出..."; cleanup; exit 0' INT —— 捕获 Ctrl+C(SIGINT)
- trap 'cleanup; exit 1' TERM HUP —— 同时捕获 SIGTERM 和 SIGHUP
- trap '' TSTP —— 忽略 SIGTSTP(Ctrl+Z),防止脚本被挂起
注意:trap 命令必须在信号可能到达前定义,通常放在脚本开头;清理函数(如 cleanup)需提前声明。
编写可靠的 cleanup 函数
优雅退出的关键不在“捕获”,而在“清理是否真正生效”。建议 cleanup 函数包含以下内容:
- 显式终止子进程(kill "$!" 2>/dev/null 或用 pkill -P $$ 清理子进程树)
- 删除临时文件或目录(rm -rf "$TMPDIR",确保变量已定义且路径安全)
- 恢复终端设置(如用 stty sane 修复被修改的终端属性)
- 记录退出日志(echo "$(date): exited gracefully" >> /var/log/myscript.log)
避免 trap 常见陷阱
实际使用中容易忽略几个关键点:
- 子 Shell 不继承 trap:在 ( ... ) 或管道中执行的命令无法触发父脚本的 trap,需在子进程中单独设置
- exit 不会触发 EXIT trap 的情况:当脚本因语法错误、未定义变量(set -u)或 command not found 中断时,EXIT trap 可能不运行,建议配合 set -e -u -o pipefail 并做防御性检查
- SIGKILL(kill -9)无法被捕获:任何 trap 对 SIGKILL 都无效,这是内核强制终止机制,无需也不应尝试处理
优雅退出不是让脚本“不死”,而是让它“死得明白、干净、可预期”。










