trap 是脚本的收尾保障
trap 用于注册信号与特殊事件的钩子:脚本退出(EXIT)时删除临时文件、收到 Ctrl+C(INT)或 TERM 时优雅退出、命令失败(ERR)时打印出错行号。写法是 trap '命令' 信号列表。要清楚 KILL 与 STOP 无法被捕获 —— 任何进程都拦不住,所以别用 trap 去拦截 kill -9。
基本信息
| 适用平台 | bash/sh/ksh(POSIX 支持 trap;ERR 与 DEBUG 是 bash 扩展) |
|---|---|
| 语法 | trap '命令' 信号...;trap - 信号 恢复默认 |
| 常用事件 | EXIT 退出、INT/TERM 中断、HUP 挂断、ERR 命令失败 |
| 相关变量 | LINENO、BASH_COMMAND、$? |
| 查看当前注册 | trap -p(bash) |
| 不可捕获 | KILL 与 STOP |
典型使用场景
- 临时目录与临时文件的清理
- 脚本被中断时优雅收尾(发送通知、释放锁)
- 失败时打印出错行号与命令,便于定位
- 批处理脚本的锁文件保障,避免残留死锁
- 封装公共函数库时统一日志与错误处理
基础用法
基础用法
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT # 正常结束或被中断都会清理
trap 'echo "[中断] 正在退出"; exit 130' INT TERM
trap 'echo "[ERR] line $LINENO: $BASH_COMMAND" >&2' ERR
set -euo pipefail # 与 ERR 陷阱配合使用
trap - EXIT # 需要时恢复默认行为
trap -p EXIT # 查看当前注册的命令(bash)信号与用途
| 信号/事件 | 触发时机 | 典型用途 |
|---|---|---|
| EXIT | 脚本退出,含被中断的情况 | 清理临时文件、释放锁 |
| INT | Ctrl+C | 打印提示并优雅退出 |
| TERM | kill 的默认信号 | 收尾通知、保存中间状态 |
| HUP | 终端挂断 | 重载配置或退出 |
| ERR | 命令返回非零(bash 扩展) | 打印行号与命令,定位失败点 |
| KILL / STOP | 无法被捕获 | 不能拦截,别写对应的 trap |
trap 后面的命令用双引号会在注册时就把变量展开,希望触发时才取值就用单引号,例如 trap 'rm -rf "$tmp"' EXIT。
EXIT 陷阱里的清理命令如果失败,可能覆盖脚本原本的退出码:关键脚本要显式保存 $? 并在清理完成后按原退出码退出。
常见组合:set -euo pipefail 配合 ERR 陷阱记录行号,EXIT 陷阱只负责清理,两者分工清楚;把清理逻辑写进 ERR 会导致部分失败场景下不执行。
信号处理函数里避免耗时操作:长时间清理会让 Ctrl+C 看起来没反应,可以先打印一行提示再执行,必要时把重活拆到后台。
POSIX sh 对 ERR 与 LINENO 的支持不如 bash 完整:脚本要在 sh 下运行时,先确认执行环境,行号定位这类能力按 bash 设计。