2009-03-26 8 views
60

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

また、これらの一時ファイルの名前と場所に関するベストプラクティスはありますか?
私が現在使用しての間ではよく分からない:

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

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

それとも、いくつかのより良い解決策があるの?

答えて

55

終了時には「trap」を、終了するにはcontrol-cで実行するように設定できます。

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

また、私のお気に入りのunix-ismsの1つは、ファイルを開き、それを開いたままで削除することです。ファイルはファイルシステム上にとどまり、読み書きできますが、プログラムが終了するとすぐにファイルは消えます。しかし、どうやってbashでそれをやるのかは分かりません。

BTW:自分の解決策を使用する代わりに、mktempに賛成するつもりです:ユーザがあなたのプログラムが巨大な一時ファイルを作成すると予想した場合、彼はTMPDIRを/ var/tmp。 mktempは、あなたの手巻きソリューション(2番目のオプション)は認識しません。私は頻繁にTMPDIR=/var/tmp gvim -d foo barを使用します。

+7

Bashでは、 'exec $ <> $ TMPFILE'はファイル記述子5を$ TMPFILEに読み書きその後、 '<&5', '>&5'と'/proc/$$/fd/5'(Linux)を使うことができます。唯一の問題は、Bashが 'seek'関数を持っていないことです。 – ephemient

+0

あなたが提供したリンクは、私が必要としていたものを最もよく説明するものですから、あなたは答えました。ありがとう – skinp

+3

'trap'に関する注意点:' SIGKILL'をトラップする方法はありません(設計通り、実行を直ちに終了します)。それが起こる可能性がある場合は、フォールバックプラン(tmpreaperなど)を持たせてください。第2に、トラップは累積的ではありません。実行するアクションが複数ある場合は、すべてが 'trap'コマンドに含まれている必要があります。複数のクリーンアップアクションに対処する1つの方法は、関数を定義することです(そして、必要に応じてプログラムが進行するにつれてそれを再定義することもできます)。そして 'trap cleanup_function EXIT'を参照してください。 –

-4

mktempで作成されたtmpファイルを削除する必要はありません。とにかく後で削除されます。

より多くのユニークなファイルを生成し、 '$$'プレフィックスを生成する場合は、mktempを使用します。そしてそれは、一時ファイルを作成して明示的に/ tmpに入れるクロスプラットフォームの方法のように見えます。

+4

誰によって削除されましたか? – innaM

+0

操作によって削除される|ファイルシステム自体が一定時間後に –

+4

マジック? cronjob?または、再起動されたSolarisマシンですか? – innaM

16

trapコマンドを使用して、スクリプトやCTRL-Cのような信号を処理する必要があります。詳細は、Advanced Bash Scripting Guideを参照してください。 basename $0を使用することは良い考えであるだけでなく、十分な一時ファイルのための部屋を提供したテンプレートを提供し、あなたの一時ファイルの

、:

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

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

trap 'rm -f $TMP1 $TMP2' EXIT 
+0

TERM/INTでトラップしないでください。 EXITのトラップ 受信した信号に基づいて終了条件を予測しようとするのは、馬鹿であり、まったくキャッチオールではありません。 – lhunath

+2

マイナーポイント:シングルバッククォートの代わりに$()を使用します。スペースを入れることができるので、$ 0の前後に二重引用符を入れてください。 –

+0

さて、このコメントではバッククォートはうまく動作しますが、それは公正な点です。 '$()'を使用するのは良いです。ダブルクォートも追加されました。 –

88

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

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

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

+20

+1 EXITのトラップを使ってください。あなたが考えることができるものは、愚かなTERM/INT/HUP /ではありません。 **あなたのパラメータの拡張を引用する**ことを覚えておいてください*また、あなたのトラップをシングル*クォートすることをお勧めします: トラップ 'rm -rf "$ TMPDIR"' EXIT – lhunath

+4

トラップはあなたのスクリプトの中で後になったら、転回のためにTMPDIRをクリーンアップして変更することにします。 – lhunath

+10

マイナーポイント:シングルバッククォートの代わりに$()を使用します。 –

3

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

私は最初、不安定なTMP1とTMP2の割り当てを引用することを考えていましたが、おそらくnot be a good ideaという2番目の考えで引用していました。

1

私は安全な方法で、/ tmp内のファイルを作成しますtempfileを使用して好む、あなたはその命名を心配する必要はありません。

tmp=$(tempfile -s "your_sufix") 
trap "rm -f '$tmp'" exit 
+0

一時ファイルは悲しいほど安全ではありませんが、非常に移植性がありません。そのため、回避するか、少なくともエミュレートするほうがよい場合がよくあります。 – lericson

3

だけで答えがbashismで選びだし心に留めておく、溶液を意味します

として
trap "{ rm -f $LOCKFILE }" EXIT 

は(シェルがdashまたはクラシックshであれば、それはCtrlキー+ cをキャッチしません)のみbashで動作しますが、あなたは、互換性を望むならば、あなたはまだあなたがTRにしたいすべての信号を列挙する必要がありますap。

また、スクリプトが信号 "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シグナル(終了の最終段階)でいくつかのコードを実行する必要がある場合

関連する問題