Bash:用 mktemp -d 给脚本准备临时工作目录

52 次浏览3 条回复

有些脚本会生成一堆中间文件,直接塞当前目录容易互相踩,也不好清理。简单一点可以每次启动时用 mktemp -d 建一个独立工作目录,退出时统一删掉。

环境:Linux/macOS,Bash 4.4+,只用常见命令。保存为 tmp-workdir-demo.sh:

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

workdir=$(mktemp -d)
cleanup() {
  rm -rf -- "$workdir"
}
trap cleanup EXIT

printf 'workdir=%s\n' "$workdir"
printf 'hello\n' >"$workdir/input.txt"
tr '[:lower:]' '[:upper:]' <"$workdir/input.txt" >"$workdir/output.txt"
cat "$workdir/output.txt"

跑一下:

chmod +x tmp-workdir-demo.sh
./tmp-workdir-demo.sh

小细节是变量引用都带上引号,删除时也加 --。如果脚本会在失败时保留现场排查,可以把 cleanup 改成判断一个环境变量,比如 KEEP_TMP=1 时只打印目录不删除。

这个写法挺实用。补一个小坑:如果以后想指定临时目录前缀,macOS/BSD 的 mktemp 更挑模板,通常要写成类似:

workdir=$(mktemp -d "${TMPDIR:-/tmp}/my-job.XXXXXX")

这样 Linux 和 macOS 都比较稳。还有就是 trap cleanup EXIT 已经能覆盖正常退出和大多数失败场景了,配合 set -Eeuo pipefail 正好。

还可以把清理函数写得稍微保守一点,避免变量意外为空时误删:

cleanup() {
  [[ -n ${workdir:-} && -d $workdir ]] || return
  rm -rf -- "$workdir"
}

这个在后面有人改脚本、把 workdir 的赋值挪来挪去时比较防手滑。mktemp -d 正常失败会直接让脚本退出,但清理函数自己多一层判断也不亏。

别熬夜啦Lv1#2

还可以把清理函数写得稍微保守一点,避免变量意外为空时误删:

cleanup() {
  [[ -n ${workdir:-} && -d $workdir ]] || return
  rm -rf -- "$workdir"
}

这个在后面有人改脚本、把 workdir 的赋值挪来挪去时比较防手滑。mktemp -d 正常失败会直接让脚本退出,但清理函数自己多一层判断也不亏。

再补一个我觉得挺顺手的小写法:如果脚本有时需要保留现场,可以让清理逻辑看环境变量,不用临时改代码。

cleanup() {
  [[ -n ${workdir:-} && -d $workdir ]] || return
  if [[ ${KEEP_TMP:-0} == 1 ]]; then
    printf 'keep temp dir: %s\n' "$workdir" >&2
    return
  fi
  rm -rf -- "$workdir"
}

平时直接跑会自动清;排查问题时这样跑:

KEEP_TMP=1 ./tmp-workdir-demo.sh

对那种中间文件很多的脚本很省事。