5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

同じファイルにサブシェルを介して読み書きした場合の動作

Posted at

@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に書いてある。つまりファイルがすべて閉じられるまでは実際に削除されないのだそうだ。

フロー的には以下になると思う(想像です)

  1. サブプロセスに対してファイルを入力リダイレクトするので(末尾、外側の< ファイル)とりあえずファイルを開く。
  2. サブシェルを起動して、rm ファイルする。まだファイルが閉じられていない(サブプロセスが動作しており、サブプロセス内で明示的に閉じられていない。0<-とかしたら閉じる?)ので実際には削除されないというか、生存中のファイルハンドラからはとりあえず読める状態。
  3. 内側のcommand > ファイルで新しいファイルを作成。ファイル名は同じだがinodeは異なるので問題ない。
  4. 読み書きして処理終了(サブプロセス終了)すると、ファイルが閉じられて、外側のファイルは実際に削除される。

尚、この書き方の場合、サブプロセス内で処理中にエラーでコケたり、システムが終了したりすると、元ファイルが消えちゃうので、安全ではないらしい。バッチに書いたらアカン感じか。
とすると、sedとかRuby、Perlでも、恐らく同様に安全じゃないのだと思うけど、どうなんだろう。

例としては微妙だが、安全にベタにやるならこんな感じだろうか
一時ファイルの置き場所を$TMPじゃなくしてるけど、そのままでもたぶん問題無いと思う。普通は。
一時ファイルを残したくないならtrapで消すといいらしい( @bsdhackシェルスクリプトを書く時に気をつけている事など

#tmpfile=$(mktemp)
tmpfile=$(mktemp -p /workdir)
if command ファイル > $tempfile; then
  mv -f $tempfile ファイル
fi

Tsukubai力が足らん気がする。

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?