mktemp コマンド
mktempコマンドは一時ファイルや一時ディレクトリを安全に作成するコマンド。
$ mktemp
/tmp/tmp.HkE3v2XsY9
実行するたびに異なるファイル名でファイルが作成され、そのパスが出力される。
単純に /tmp/tmpfile のような固定名で一時ファイルを作成すると、複数のプロセスが同じファイルを上書きしてしまう競合や、シンボリックリンク攻撃などのセキュリティリスクがある。mktempはランダムな文字列を含むファイル名を生成するため、これらの問題を回避できる。
基本的な使い方
一時ファイルの作成
$ TMPFILE=$(mktemp)
$ echo "TMPFILE: $TMPFILE"
TMPFILE: /tmp/tmp.HkE3v2XsY9
$()でコマンド置換を使い、変数にパスを格納して使用する。
一時ディレクトリの作成(-d)
-dオプションで一時ディレクトリを作成できる。
$ TMPDIR=$(mktemp -d)
$ echo "TMPDIR: $TMPDIR"
TMPDIR: /tmp/tmp.aBcDeFgHiJ
$ ls -la "$TMPDIR"
total 8
drwx------ 2 user user 4096 Jun 24 21:00 .
drwxrwxrwt 8 root root 4096 Jun 24 21:00 ..
作成されたディレクトリは700(所有者のみアクセス可)の権限で作成される。
テンプレートの指定
ファイル名のパターンを指定できる。末尾のXがランダムな文字に置き換えられる。
$ mktemp /tmp/myapp.XXXXXX
/tmp/myapp.k7Lm2N
Xは最低6個指定する必要がある。
プレフィックスとサフィックスを含むテンプレート
$ mktemp /tmp/myapp_XXXXXX.log
/tmp/myapp_k7Lm2N.log
サフィックスにも対応しているため、拡張子を付けた一時ファイルを作成できる。
ディレクトリの指定(-p)
-pオプションで作成先のディレクトリを指定できる。
$ mktemp -p /var/tmp
/var/tmp/tmp.Xz1aB2cDeF
テンプレートと組み合わせることもできる。
$ mktemp -p /var/tmp myapp.XXXXXX
/var/tmp/myapp.Xz1aB2
dry-run(-u)
-u(--dry-run)オプションはファイルやディレクトリを実際には作成せず、生成されるはずの名前だけを出力する。
$ mktemp -u
/tmp/tmp.Tpwo1Yjrl6
$ ls /tmp/tmp.Tpwo1Yjrl6
ls: cannot access '/tmp/tmp.Tpwo1Yjrl6': No such file or directory
テンプレートと組み合わせることもできる。
$ mktemp -u /tmp/myapp.XXXXXX
/tmp/myapp.8Gh7QH
--dry-runオプションにより現時点で存在しない名前のみ取得できる。
注意点として名前を取得してからファイルを作成するまでの間に別のプロセスが同じ名前を使う可能性がある。
テンプレートと組み合わせて独自のprefixやsuffixを含むファイル名のパターンを指定し、他のコマンドが生成するファイル名と重複する可能性を減らす必要がある。
シェルスクリプトでの使い方
trap との組み合わせでクリーンアップする
スクリプト終了時に一時ファイルを確実に削除するには、trapコマンドと組み合わせる。
#!/bin/bash
set -euo pipefail
TMPFILE=$(mktemp)
trap 'rm -f "$TMPFILE"' EXIT
# TMPFILE を使った処理
some_command > "$TMPFILE"
process_results "$TMPFILE"
trap ... EXITを設定すると、スクリプトがエラーで途中終了した場合でも一時ファイルが削除される。
一時ディレクトリの場合はrm -rfを使う。
#!/bin/bash
set -euo pipefail
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT
# TMPDIR 内でファイルを操作
cp important_file.txt "$TMPDIR/"
process_dir "$TMPDIR"
複数の一時ファイルをまとめて削除する
#!/bin/bash
set -euo pipefail
TMPFILE1=$(mktemp)
TMPFILE2=$(mktemp)
trap 'rm -f "$TMPFILE1" "$TMPFILE2"' EXIT
command1 > "$TMPFILE1"
command2 > "$TMPFILE2"
diff "$TMPFILE1" "$TMPFILE2"
trapのコマンドに複数ファイルを指定してまとめて削除できる。
具体例
コマンドの出力を比較する
2つのコマンドの出力を差分確認する場合に便利である。
$ FILE1=$(mktemp)
$ FILE2=$(mktemp)
$ cat /etc/hosts | sort > "$FILE1"
$ cat /etc/hosts.bak | sort > "$FILE2"
$ diff "$FILE1" "$FILE2"
$ rm -f "$FILE1" "$FILE2"
APIのレスポンスを一時保存して処理する
#!/bin/bash
set -euo pipefail
TMPFILE=$(mktemp)
trap 'rm -f "$TMPFILE"' EXIT
curl -s https://api.example.com/data > "$TMPFILE"
jq '.items[]' "$TMPFILE"
curlのレスポンスを一時ファイルに保存し、jqで複数回パースする場合に使用する。
ファイルをインプレース編集する
sed -iのインプレース編集をより安全に実装できる。
#!/bin/bash
set -euo pipefail
TARGET="config.txt"
TMPFILE=$(mktemp)
trap 'rm -f "$TMPFILE"' EXIT
sed 's/old/new/g' "$TARGET" > "$TMPFILE"
mv "$TMPFILE" "$TARGET"
mvはアトミック操作のため、処理中にファイルが壊れるリスクを減らせる。trapの前にmvが成功すると$TMPFILEは存在しなくなるが、rm -fは存在しないファイルに対してエラーを出さないため問題ない。
macOS と Linux の違い
macOS(BSD版)とLinux(GNU版)ではmktempの挙動が一部異なる。
テンプレートの指定方法
Linux(GNU版)ではテンプレートをそのまま引数に渡す。
# Linux
$ mktemp /tmp/myapp.XXXXXX
/tmp/myapp.k7Lm2N
macOS(BSD版)では-tオプションでテンプレートのプレフィックスを指定する。Xのパターンは自動で付与される。
# macOS
$ mktemp -t myapp
/tmp/myapp.k7Lm2N
両対応のシェルスクリプト
環境依存を避けるには、プレフィックスなしでmktempのみを使うか、TMPDIR環境変数を利用する方法がある。
# macOS / Linux 共通
TMPFILE=$(mktemp)
TMPDIR_PATH=$(mktemp -d)
引数なしのmktempと-dオプションはmacOS・Linux両方で同じ動作である。
