必须在脚本开头注册 trap,同时捕获 int、term、hup 和 exit 四个信号/事件:int 和 term 处理用户中断与常规终止,hup 应对 ssh 断开或 systemd 重启,exit 确保所有退出路径(包括正常结束)均执行清理。

必须在脚本开头就注册 trap,否则 Ctrl+C 或 kill 发出的信号会直接终止进程,跳过清理逻辑。
捕获 SIGINT(Ctrl+C)和 SIGTERM(kill 默认信号)
绝大多数中断场景来自用户按 Ctrl+C(触发 SIGINT)或外部调用 kill $PID(默认发 SIGTERM)。这两个信号必须同时捕获,缺一不可。
-
trap后的命令或函数会在信号到达时立即执行,**不等待当前命令结束**(比如正在运行cp或sleep,也会被中断后跳转到 trap) - 推荐用函数封装清理逻辑,避免单行命令中引号嵌套出错:例如
trap 'rm -f "$TMPDIR"; exit 1' INT TERM中的双引号变量不会展开,应改用函数 - 信号名可省略
SIG前缀:INT等价于SIGINT,TERM等价于SIGTERM
为什么一定要加 EXIT 陷阱
EXIT(等价于 0)不是真实信号,而是在 shell 退出前**必然触发**的伪事件。它能兜底所有未被显式捕获的退出路径:
- 脚本自然运行结束(最后一条命令执行完)
- 遇到
exit显式退出 - 被
SIGKILL杀死(此时INT/TERMtrap 不生效,但EXIT仍会运行) - 语法错误导致解析失败(如未闭合引号)——注意:这种情况下
EXIT通常不触发,属于例外
实际写法:trap cleanup EXIT,其中 cleanup 是你定义的函数;不要只依赖 INT 和 TERM,否则正常跑完脚本时临时文件就漏删了。
在无 root/sudo 权限的环境(云容器、VPS、隔离主机)中安装并配置 OpenClaw 浏览器工具的 headless Chrome。适用场景:...
高并发脚本必须捕获 SIGHUP
SSH 断开、systemd 重启服务、终端意外关闭时,父 shell 收到的是 SIGHUP,而不是 INT 或 TERM。它极易被忽略,但后果严重:
- 后台子进程(如
curl &、rsync --daemon &)可能变成孤儿进程继续运行,占用资源 - 锁文件、临时目录、socket 文件残留,下次启动失败
-
systemd的stop操作默认发送SIGHUP,没捕获就等于“无响应退出” -
trap必须在启动任何子进程前设置,否则子进程可能已继承原始信号行为,脱离控制
容易踩的坑和绕不开的限制
trap 不是万能的,几个硬性限制必须提前知道:
-
SIGKILL(kill -9)和SIGSTOP永远无法被捕获或忽略,这是内核强制行为 - 子 shell(如
(...)或管道中的命令)不继承父 shell 的trap,需在子 shell 内重新设置 - 用
exec替换当前进程(如exec python3 server.py)后,原有trap全部失效 - 函数内定义的
trap只在该函数作用域有效;全局trap应写在脚本最顶部,紧挨#!/bin/bash之后
真正关键的不是“能不能捕获”,而是“是否覆盖了所有退出入口”。INT、TERM、HUP、EXIT 这四个点齐备,才能让脚本在各种断电、断网、误操作下都不留尾巴。










