在日常的 Linux 自动化脚本编写中,我们往往更关注"主流程是否跑通",却容易忽略一个问题:脚本是如何结束的 。
现实环境中,脚本很少总是"正常跑完"------它可能被用户中断、被系统终止、被服务管理器停止,甚至在异常情况下提前退出。如果没有针对这些场景做处理,临时文件、锁、子进程和系统状态就可能被遗留下来,久而久之演变成难以排查的问题。
Linux 提供的信号(Signal)机制 ,正是操作系统与进程之间进行"异常沟通"的基础;而 Shell 中的 trap,则是我们在脚本层面对这些信号进行响应的核心工具。理解并正确使用 trap,不仅可以让脚本在被打断时优雅退出,更是判断一个脚本是否具备工程质量的重要标准之一。

一、为什么需要了解信号与 trap?
在真实的自动化运维、测试、部署脚本中,**脚本很少是"跑完就结束"**的:
- 用户按下 Ctrl + C
- 系统重启 / 服务停止
- Docker 容器被 stop
- 脚本被 kill 掉
- 发生异常提前退出
如果不做处理,常见问题包括:
- 临时文件残留
- 锁文件未释放
- 子进程变成僵尸进程
- 硬件/设备状态未恢复
👉 Linux 信号 + trap 是解决这些问题的标准机制。
二、Linux 常见信号速查表
1️⃣ 最常用、最重要的信号
| 信号 | 编号 | 含义 | 典型触发场景 |
|---|---|---|---|
| SIGINT | 2 | 中断 | Ctrl + C |
| SIGTERM | 15 | 终止(可捕获) | kill / systemd stop |
| SIGQUIT | 3 | 退出并生成 core | Ctrl + \ |
| SIGKILL | 9 | 强制终止 | kill -9 |
| SIGSTOP | 19 | 强制暂停 | kill -STOP |
📌 重点记忆:
SIGINT/SIGTERM可 trapSIGKILL/SIGSTOP不可 trap
2️⃣ 脚本中常见的其他信号
| 信号 | 含义 | 说明 |
|---|---|---|
| SIGHUP | 终端挂起 | SSH 断开、守护进程 |
| SIGPIPE | 管道断开 | 管道下游提前退出 |
| SIGALRM | 定时器到期 | alarm / timeout |
| SIGCHLD | 子进程退出 | 回收子进程 |
三、trap 是什么?
脚本中优雅退出与资源清理的核心机制
trap 用来捕获指定信号,并在信号发生时执行自定义命令或函数。
四、trap 基本语法
1️⃣ 标准语法
bash
trap 'command' SIGNAL [SIGNAL ...]
也可以写成:
bash
trap command SIGNAL [SIGNAL ...]
2️⃣ 使用函数(推荐)
bash
cleanup() {
echo "Doing cleanup..."
}
trap cleanup SIGINT SIGTERM
📌 工程脚本中,99% 推荐用函数而不是单行命令。
五、最重要的 trap 使用场景
场景 1:脚本被中断时清理资源(最常见)
bash
cleanup() {
rm -f /tmp/tmpfile
}
trap cleanup SIGINT SIGTERM
✔ Ctrl + C
✔ kill
✔ 服务停止
都会触发清理
场景 2:保证脚本正常退出也执行清理(EXIT)
bash
cleanup() {
echo "Exit cleanup"
}
trap cleanup EXIT
| 情况 | 是否触发 |
|---|---|
| 正常结束 | ✅ |
| exit | ✅ |
| return | ✅ |
⚠️ EXIT ≠ 信号,但非常实用
场景 3:组合使用(推荐写法)
bash
trap cleanup SIGINT SIGTERM EXIT
👉 无论异常还是正常退出,都执行 cleanup
场景 4:子进程回收(自动化脚本必备)
bash
cleanup() {
pkill -P $$
}
trap cleanup SIGINT SIGTERM
避免:
- 后台进程遗留
- 僵尸进程
六、trap 的高级用法
1️⃣ 忽略某个信号
bash
trap '' SIGINT
Ctrl + C 将被忽略(慎用)
2️⃣ 恢复默认行为
bash
trap - SIGINT
3️⃣ 捕获多个信号
bash
trap cleanup SIGINT SIGTERM SIGHUP
4️⃣ 使用信号编号(不推荐)
bash
trap cleanup 2 15
七、哪些信号 trap 不住?
bash
SIGKILL (9) ❌
SIGSTOP ❌
原因:这是内核级强制控制信号,用户态程序无法拦截
八、总结
Linux 信号定义了"程序如何被打断", trap 决定了"脚本被打断时如何善后"。
写好 trap,是区分"能跑的脚本"和"工程级脚本"的关键。