Shell脚本精读 · S11-01 | 信号与 `trap`:清理临时文件与优雅退出

📅 2026/7/9 15:36:45
Shell脚本精读 · S11-01 | 信号与 `trap`:清理临时文件与优雅退出
模块S11 信号、陷阱与健壮性篇号S11-01 / 42预计阅读45 分钟主线Bash文章目录本篇目标30 秒速览正文1. 为什么需要 trap2. 基本语法3. EXIT最常用4. 临时文件标准模式5. INT 与 TERM优雅退出6. 忽略与恢复信号7. ERRBash 常配合 set -e8. RETURNBash函数返回时9. trap 字符串与引号10. 与 set -e、exit 的关系11. 读脚本时的常见片段11.1 锁文件11.2 子进程与 trap11.3 被 source 的库12. 注意点小结读脚本检查清单练习判断题实操题改错题下一篇预告本篇目标掌握trap在脚本收到信号或退出时执行清理命令。会写trap cleanup EXIT、处理CtrlCINT、TERM配合mktemp删除临时文件并理解ERR/RETURNBash的适用场景。读完能读懂运维脚本里的「无论成功失败都清理」写法。30 秒速览信号内核/终端发给进程的异步通知如INT中断、TERM终止。trap 命令 信号列表收到信号或事件时执行命令字符串形式。EXITShell退出时触发成功、exit、set -e失败均可适合做清理。临时文件tmp$(mktemp)后trap rm -f $tmp EXIT。忽略信号trap INT恢复默认trap - INT。trap处理器里慎用exit可能打断其它清理。正文1. 为什么需要trap脚本可能被CtrlC打断被kill发TERM因set -e中途失败退出正常exit 0结束若只在中途写了rm $tmp异常退出时临时文件会残留。trap ... EXIT让清理在离开 Shell 前尽量执行一次。2. 基本语法trapecho caught; cleanupEXIT INTTERM部分含义trap内置命令命令触发时由 Shell解析执行的字符串信号名EXIT、INT、TERM、HUP、ERR等查看信号编号trap-l# 或kill-l常见名与含义信号典型来源INTCtrlCTERMkill PID、systemctl stopHUP终端挂断、部分服务重载EXIT非信号Shell退出事件Bash 等3.EXIT最常用#!/usr/bin/env bashset-euopipefailtmpdir$(mktemp-d)traprm -rf $tmpdirEXITechowork in$tmpdir# 正常结束或 set -e 失败退出都会 rm -rfEXIT在当前 Shell 退出前运行包括exit n、脚本末尾、set -e触发退出。注意用$tmpdir引号在trap 触发时再展开变量单引号包住 trap 字符串时变量在执行 trap 时解析。trap可多次注册同信号后注册的会覆盖或追加Bash 对同信号覆盖前一个 handler。trapecho firstEXITtrapecho secondEXIT# 最终 EXIT 时通常只跑 secondBash4. 临时文件标准模式tmp$(mktemp)traprm -f $tmpEXIT# 多文件tmps()cleanup(){rm-f${tmps[]}}trapcleanup EXITtmps($(mktemp))函数作 handler可读性更好cleanup(){rm-rf${tmpdir:-}}trapcleanup EXIT INTTERMtmpdir$(mktemp-d)5.INT与TERM优雅退出running1cleanup(){running0rm-f$lockfileechointerrupted, cleaned2}trapcleanup INTTERMEXITwhile((running));dodo_worksleep1done用户CtrlC→INT→cleanup若也注册了EXIT注意INT 后仍会 EXIT避免cleanup 跑两次——可合并为同一 handler 并加幂等见下。合并写法cleanup(){[[${_cleaned:-}1]]return_cleaned1rm-rf$tmpdir}trapcleanup EXIT INTTERM6. 忽略与恢复信号trapINT# 忽略 CtrlC慎用trap- INT# 恢复默认 INT 行为长时间计算不想被误中断时偶见trap INT要有其它退出途径。7.ERRBash 常配合set -eset-euopipefailtrapecho failed at line $LINENO 2ERRERR某条命令返回非 0 时触发set -e生效上下文内规则有例外S01-04。用于打日志不替代set -e。不要在 ERR trap 里再跑会失败的命令而不处理可能循环。8.RETURNBash函数返回时work(){localtmptmp$(mktemp)traprm -f $tmpRETURN...}函数返回时删函数内临时文件不影响全局EXIThandlerS08-02 提过。9.trap字符串与引号tmpdir$(mktemp-d)# 对触发时展开 $tmpdirtraprm -rf $tmpdirEXIT# 错定义 trap 时就把 $tmpdir 写死进空串若当时未赋值traprm -rf$tmpdirEXIT# 定义时展开易错# 传信号名给 cleanuptrapcleanup INTINTcleanup(){echogot signal2rm-rf$tmpdir}习惯handler 用单引号包主体让$变量在触发时展开。10. 与set -e、exit的关系traprm -f $tmpEXITfalse# set -e → 退出前仍跑 EXIT trapcleanup(){rm-f$tmpexit1# 在 trap 里 exit 要小心}trap里exit可能改变最终退出码清理函数一般return 0让外层exit决定码。11. 读脚本时的常见片段11.1 锁文件lock/tmp/myapp.locktraprm -f $lockEXITecho$$$lock11.2 子进程与 traptrap对当前 Shell 有效子脚本、( )子 shell需各自注册。bash child.sh里的 trap 不保护父脚本。11.3 被 source 的库trap会改当前 Shell库文件随意trap EXIT可能污染调用方——库应文档化或提供setup_trap可选函数S08-03。12. 注意点小结点说明EXIT 几乎总要清临时文件、释锁handler 幂等INT EXIT 可能都触发引号触发时展开路径变量不要只 trap INT仍建议EXIT兜底调试trap echo SIG DEBUG刷屏生产慎用读脚本检查清单是否有mktemp而无trap ... EXITcleanup是否 INT/TERM/EXIT 重复执行需幂等trap 字符串里变量是否会在触发时正确展开source的库是否擅自trap了全局 EXITERR trap是否用于日志而非掩盖错误练习判断题trap cmd EXIT只在exit 0时执行。CtrlC 通常向前台进程组发送 SIGINT。trap rm -f $f EXIT中若f在 trap 定义后才赋值触发时仍能删对文件单引号 trap 字符串。trap INT表示忽略中断。子 shell( trap echo x EXIT; false )退出后父 shell 的 trap 不受影响。参考答案错任何退出方式含失败。对。对触发时展开$f。对。对。实操题补全创建临时文件写入hello脚本结束前自动删除含set -e失败路径。#!/usr/bin/env bashset-euopipefail# 补全 trap 与 mktempechohello???cat???参考答案tmp$(mktemp)traprm -f $tmpEXITechohello$tmpcat$tmp改错题tmpdir$(mktemp-d)traprm -rf$tmpdirEXITcd$tmpdir参考tmpdir$(mktemp-d)traprm -rf $tmpdirEXITcd$tmpdir双引号 trap 在定义时展开$tmpdir若写法无误也易在tmpdir未赋值时定义 trap。应用单引号让路径在退出时展开。另若cd失败EXIT 仍会删 tmpdir通常期望。下一篇预告S11-02《健壮性清单锁、重试、超时与幂等》— 与trap配合的实战习惯。