シェルスクリプトで一時ファイルを安全に扱う3つのコツ

  • 77
    いいね
  • 6
    コメント

はじめに

シェルスクリプトで一時ファイルを使うことはよくあることだと思うけど、毎度やり方を忘れてしまうのでまとめておく。
(主に自分が)すぐに使えるように一番下にテンプレートを貼っておく。

動作確認環境

Ubuntu 12.04.4 LTS

コツ

mktemp を使用する

例えば以下のように固有のファイル名を使ってしまったら、他のプロセスとの競合などが問題になる。

tmpfile=/tmp/hoge

mktemp コマンドを使うことで、/tmp ディレクトリ以下にランダムな?文字列のファイルが生成されて、他のプロセスとの競合が発生しない。
さらに、パーミッションを700で生成してくれるので、他のユーザから見られることもない。
(シンボリックリンク攻撃の脆弱性も防げるらしいがこれは勉強不足のため分からない・・・)

tmpfile=`mktemp`

一時ファイルを大量に置きたい場合は、-d オプションをつけて一時ディレクトリを生成すると良い。

tmpdir=`mktemp -d`

明示的に削除しない場合は、マシンの再起動時などのタイミングで暗黙的に削除される。

set -eu をつける

オプション 意味
e シェルスクリプト実行中にエラーが起きた時に終了してくれる。
u 未定義のシンボルを参照した時に終了してくれる。

一時ファイルを使わないシェルスクリプトでもこのオプションは使用することは多いと思う。
これがあることで、次に説明する trap を使って、シェルスクリプトが途中で終了してしまった時に一時ファイルを削除してくれる。

trap を使う

trap の説明に関しては、shellのtrapについて覚え書き に詳しく書いてあるので、割愛する。

下記コマンドでは、SIGHUP SIGINT SIGTERM のシグナルが発生したときに、一時ファイルを削除してくれる。
先述したとおり、いつかは削除してくれるのだけど、ゴミを残したままスクリプトが終了することは行儀が悪いのでやっておくべき。

trap '[[ -n ${tmpfile-} ]] && rm -f "$tmpfile"' SIGHUP SIGINT SIGTERM
シグナル 意味
SIGHUP 端末終了時に発生
SIGINT Control-C による中断で発生
SIGTERM kill コマンドでデフォルト送られるプロセス終了時に発生

テンプレート

#!/bin/bash

set -eu
atexit() {
      [[ -n $tmpfile ]] && rm -f "$tmpfile"
}
tmpfile=`mktemp`
trap atexit EXIT
trap 'trap - EXIT; atexit; exit -1' SIGHUP SIGINT SIGTERM

# ここから先に一時ファイルを操作する処理を記述する

trap で EXIT を受けて通常終了時にもファイルを削除するようにしている。
そして、SIGHUP SIGINT SIGTERM を受けた時は、ファイルを削除した上でエラーコードを返して終了する。

参考URL

安全な一時ファイルの作成と削除の方法
shellのtrapについて覚え書き
シェルスクリプトを書くときはset -euしておく

おわりに

一時ファイルを使用するシェルスクリプトを書こうとする度に検索していたのだが、テンプレートを作成したから、今後はこれを参考にできる。
ここまで書いて思ったんだけど、emacs のスニペットにすればいいのか・・・