@bsdhack のファイルのオーバーライドが元記事となります。
sort (1) の -o、sedやRuby/Perlの-i、つまり引数のファイルを処理した上で元のファイルと置き換えるような動作の場合の話です。以下の通りのコマンドです(コピペです)。
(rm ファイル; command > ファイル) < ファイル
疑問
@bsdhack のファイルのオーバーライドを読んで、末尾の文が少し不思議だった。
対して rm (1) がある場合は外側のシェルが O_RDONLY で open (2) するファイルと内側のシェルが O_RDONLY|O_TRUNC で open (2) するファイルの inode が異なるので OS は別なファイルと見なすので O_TRUNC による truncate は影響を受けない。
inodeが異なるのはいいけど、外側のinodeは削除したのに読み込み可能なのか。安全なのだろうか、というあたり。
答え
答えはWikipedia:inodeに書いてある。つまりファイルがすべて閉じられるまでは実際に削除されない
のだそうだ。
フロー的には以下になると思う(想像です)
- サブプロセスに対してファイルを入力リダイレクトするので(末尾、外側の
< ファイル
)とりあえずファイルを開く。 - サブシェルを起動して、
rm ファイル
する。まだファイルが閉じられていない(サブプロセスが動作しており、サブプロセス内で明示的に閉じられていない。0<-とかしたら閉じる?)ので実際には削除されないというか、生存中のファイルハンドラからはとりあえず読める状態。 - 内側の
command > ファイル
で新しいファイルを作成。ファイル名は同じだがinodeは異なるので問題ない。 - 読み書きして処理終了(サブプロセス終了)すると、ファイルが閉じられて、外側のファイルは実際に削除される。
尚、この書き方の場合、サブプロセス内で処理中にエラーでコケたり、システムが終了したりすると、元ファイルが消えちゃうので、安全ではないらしい。バッチに書いたらアカン感じか。
とすると、sedとかRuby、Perlでも、恐らく同様に安全じゃないのだと思うけど、どうなんだろう。
例としては微妙だが、安全にベタにやるならこんな感じだろうか
一時ファイルの置き場所を$TMPじゃなくしてるけど、そのままでもたぶん問題無いと思う。普通は。
一時ファイルを残したくないならtrapで消すといいらしい( @bsdhack のシェルスクリプトを書く時に気をつけている事など)
#tmpfile=$(mktemp)
tmpfile=$(mktemp -p /workdir)
if command ファイル > $tempfile; then
mv -f $tempfile ファイル
fi
Tsukubai力が足らん気がする。