web-dev-qa-db-ja.com

予期しないbash出口で作成された一時ファイルを削除する

Bashスクリプトから一時ファイルを作成しています。処理の最後にそれらを削除していますが、スクリプトが非常に長い時間実行されているため、実行中にそれを強制終了するか、単にCTRL-Cを押すと、一時ファイルは削除されません。
これらのイベントをキャッチし、実行が終了する前にファイルをクリーンアップする方法はありますか?

また、これらの一時ファイルの名前と場所について、ある種のベストプラクティスはありますか?
現在、使用するかどうかはわかりません:

TMP1=`mktemp -p /tmp`
TMP2=`mktemp -p /tmp`
...

そして

TMP1=/tmp/`basename $0`1.$$
TMP2=/tmp/`basename $0`2.$$
...

それとも、もっと良い解決策がありますか?

83
skinp

trap 」を設定して、終了時に実行するか、control-cで実行してクリーンアップできます。

trap "{ rm -f $LOCKFILE; }" EXIT

別の方法として、私のお気に入りのunix-ismの1つは、ファイルを開き、それを開いたままにしてから削除することです。ファイルはファイルシステムに残り、読み書きできますが、プログラムが終了するとすぐにファイルはなくなります。ただし、bashでどのように行うかはわかりません。

ところで:私はあなた自身のソリューションを使用する代わりにmktempを支持します:ユーザーがあなたのプログラムが巨大な一時ファイルを作成しようとしている場合、彼はTMPDIRを/ varのようなより大きなどこかに設定したいかもしれません/ tmp。 mktempはそれを認識しますが、手動ロールソリューション(2番目のオプション)は認識しません。私はよくTMPDIR=/var/tmp gvim -d foo bar、 例えば。

81
Paul Tomblin

私は通常、すべての一時ファイルを配置するディレクトリを作成し、その後すぐに、スクリプトの終了時にこのディレクトリをクリーンアップするためにEXITハンドラーを作成します。

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

すべての一時ファイルを$MYTMPDIRの下に置くと、ほとんどの状況でスクリプトが終了するときにすべての一時ファイルが削除されます。ただし、SIGKILL(kill -9)を使用してプロセスを強制終了すると、すぐにプロセスが強制終了されるため、その場合はEXITハンドラーは実行されません。

104
Chris AtLee

trap コマンドを使用して、スクリプトまたはCTRL-Cなどのシグナルの終了を処理します。詳細については、 Greg's Wiki を参照してください。

一時ファイルの場合、basename $0は、十分な一時ファイル用のスペースを提供するテンプレートを提供するのと同様に、良いアイデアです。

tempfile() {
    tempprefix=$(basename "$0")
    mktemp /tmp/${tempprefix}.XXXXXX
}

TMP1=$(tempfile)
TMP2=$(tempfile)

trap 'rm -f $TMP1 $TMP2' EXIT
21
Brian Campbell

選択した答えはbashismであることに注意してください。これは、

trap "{ rm -f $LOCKFILE }" EXIT

bashでのみ動作します(シェルがdashまたはクラシックshの場合、Ctrl + cをキャッチしません)が、互換性が必要な場合は、トラップするすべてのシグナルを列挙する必要があります。

また、スクリプトが終了すると、シグナル "0"(別名EXIT)のトラップが常に実行されるため、trapコマンドが2回実行されることに注意してください。

EXITシグナルがある場合、すべてのシグナルを1行にスタックしない理由。

理解を深めるために、変更なしで異なるシステム間で機能する次のスクリプトを見てください。

#!/bin/sh

on_exit() {
  echo 'Cleaning up...(remove tmp files, etc)'
}

on_preExit() {
  echo
  echo 'Exiting...' # Runs just before actual exit,
                    # Shell will execute EXIT(0) after finishing this function
                    # that we hook also in on_exit function
  exit 2
}


trap on_exit EXIT                           # EXIT = 0
trap on_preExit HUP INT QUIT TERM STOP PWR  # 1 2 3 15 30


sleep 3 # some actual code...

exit 

このソリューションは、最終出口(preExit関数)の直前に実際のシグナルの発生時にコードの一部を実行でき、必要に応じて実際のEXITシグナルでコードを実行できるため、より制御しやすくなります(出口)

7
Alex

予測可能なファイル名を$$で使用する代わりの方法は、セキュリティホールを広げることであり、決して使用することを考えるべきではありません。たとえそれがあなたのシングルユーザーPC上の単純な個人的なスクリプトであっても。それはあなたが得るべきではない非常に悪い習慣です。 BugTraq は「安全でない一時ファイル」インシデントでいっぱいです。一時ファイルのセキュリティ面の詳細については、 herehere 、および here を参照してください。

私は当初、安全でないTMP1とTMP2の割り当てを引用することを考えていましたが、2番目の考えではおそらく 良いアイデアではない になるでしょう。

3
hlovdal

安全な方法で/ tmpにファイルを作成するtempfileを使用することを好み、その命名について心配する必要はありません。

tmp=$(tempfile -s "your_sufix")
trap "rm -f '$tmp'" exit
1
Ruslan Kabalin