功能说明
脚本启用 set -euo pipefail 与 umask 077,先用 mktemp -d 在 TMPDIR 下创建唯一目录,再写入包含时间、脚本路径和进程号的报告并打印。无论正常结束、命令失败还是收到 INT/TERM,EXIT trap 都会调用 cleanup 删除目录。设置 SIMULATE_FAILURE=1 可以主动以状态 1 退出,用来验证清理不会掩盖原始失败。
关键代码
cleanup 首先用 local status=$? 捕获进入 trap 时的退出码,然后执行 trap - EXIT INT TERM,避免 cleanup 内部再次触发 trap 或递归。删除前同时检查变量非空且目录存在,rm -rf 后加 || true,确保清理失败不改变退出原因。INT 映射为 130,TERM 映射为 143,这是 shell 的惯例。最后 exit “$status” 让脚本以原始状态结束。
使用注意
信号处理期间不要在 cleanup 中执行耗时或可能再次失败的操作。trap 只能覆盖脚本自身收到的信号,子进程可能需要单独转发。临时目录应放在同一文件系统,避免 mv 跨设备失败。不要把用户输入直接拼进 rm 路径,示例只删除 mktemp 返回的目录。Windows 的 Git Bash 对信号支持有限,行为可能与 Linux 不同。
验证方式
直接运行脚本,应看到报告内容和清理前的目录路径,结束后该目录不存在。执行 SIMULATE_FAILURE=1 bash 脚本,退出码应为 1,同时目录仍被删除。可在脚本运行期间发送 Ctrl+C,确认退出码为 130 且临时目录被清理。再用 set -x 观察 trap 是否只执行一次。
代码示例
#!/usr/bin/env bash
set -euo pipefail
umask 077
work_dir=""
cleanup() {
local status=$?
trap - EXIT INT TERM
if [[ -n "$work_dir" && -d "$work_dir" ]]; then
rm -rf -- "$work_dir" || true
fi
exit "$status"
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
work_dir="$(mktemp -d "${TMPDIR:-/tmp}/report.XXXXXX")"
report="$work_dir/report.txt"
{
printf 'generated_at=%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
printf 'script=%s\n' "${BASH_SOURCE[0]}"
printf 'pid=%s\n' "$"
} > "$report"
cat -- "$report"
printf 'work_dir_before_cleanup=%s\n' "$work_dir"
if [[ "${SIMULATE_FAILURE:-0}" == "1" ]]; then
printf 'simulated failure; cleanup still runs\n' >&2
exit 1
fi