Bash:临时目录配个 `trap`,脚本退出时自动清理

42 次浏览4 条回复

写脚本时如果用了临时目录,最后手动 rm -r 很容易因为中途报错被跳过。可以用 trap 挂到 EXIT,脚本正常结束或提前退出时都跑一次清理。

环境:bash 4+,Linux/macOS 常见环境可以试:

#!/usr/bin/env bash
set -euo pipefail

dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT

printf 'hello\n' > "$dir/data.txt"
cat "$dir/data.txt"

# 这里就算后面某步失败,退出时也会清掉临时目录

如果想看效果明显一点,可以临时加一行:

echo "temp dir: $dir"

我一般会把 mktemp -dtrap 放得很近,免得目录建出来之后忘了兜底。要注意变量加引号,另外 rm -rf 里的路径最好只用 mktemp 生成的那个变量,别拼太复杂。

这里还有个小细节:如果脚本里会收到 Ctrl+C,EXIT 也会跑,临时目录一般能清掉。

不过要是清理逻辑以后变复杂了,我会单独写个函数,可读性好一点:

cleanup() {
  rm -rf "$dir"
}
trap cleanup EXIT

短脚本用你这个写法就很利落。

还有一种我挺常用的写法,是先判断目录变量不为空再删,主要防手滑:

cleanup() {
  [[ -n "${dir:-}" && -d "$dir" ]] && rm -rf "$dir"
}
trap cleanup EXIT

对小脚本来说有点啰嗦,但如果后面会改来改去,这个保护还挺安心。

我还会偶尔加个调试开关,出错时先把目录留着看一眼,不然现场被清掉有点难查:

keep_tmp=${KEEP_TMP:-0}
cleanup() {
  if [[ "$keep_tmp" == 1 ]]; then
    echo "keep temp dir: $dir" >&2
    return
  fi
  rm -rf "$dir"
}
trap cleanup EXIT

平时默认清理,需要排查时 KEEP_TMP=1 ./script.sh,小脚本里挺省心。

补一个容易踩的小坑:同一个信号后面再写一次 trap,不是追加,是覆盖前面的处理。

trap 'echo first' EXIT
trap 'echo second' EXIT
# 退出时只会打印 second

所以脚本里如果多个地方都想挂清理动作,最好集中到一个 cleanup() 里,不然前面那个清理可能悄悄没了。