Shell 脚本中的信号与 trap:从 Ctrl+C 到优雅退出

在日常的 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 可 trap
  • SIGKILL / 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,是区分"能跑的脚本"和"工程级脚本"的关键。

相关推荐
LawrenceLan2 小时前
Flutter 零基础入门(十):final、const 与不可变数据
开发语言·flutter·dart
Frdbio2 小时前
环腺苷酸(cAMP)ELISA检测试剂盒
linux·人工智能·python
生产队队长2 小时前
Linux:awk进行行列转换操作
android·linux·运维
linweidong2 小时前
在Ubuntu新版本安装gcc4.8等老版本环境
linux·运维·ubuntu
源代码•宸2 小时前
Leetcode—1266. 访问所有点的最小时间【简单】
开发语言·后端·算法·leetcode·职场和发展·golang
遇见~未来2 小时前
JavaScript数组全解析:从本质到高级技巧
开发语言·前端·javascript
南屿欣风2 小时前
Sentinel 熔断规则 - 异常比例(order & product 示例)笔记
java·开发语言
石像鬼₧魂石2 小时前
80 端口(Web 服务)渗透测试完整总结(含踩坑 + 绕过 + 实战流程)
linux·运维·服务器·前端·网络·阿里云
u0104058362 小时前
使用Java实现高性能的异步编程:CompletableFuture与Reactive Streams
java·开发语言