web-dev-qa-db-ja.com

rm <dir> / *と同時に<dir>に出力するのはどれほど安全か

ディレクトリの内容をすべて削除して、そこに新しいファイルを作成する必要がある場合があります。このようなことをして、すべての新しいファイルがそのまま残ることを期待できますか?

% rm -rf regression/* & ( sleep 10 ; run_regression )

どこ run_regression出力ファイルにタイムスタンプを付けて、一意の名前を付け、regressionに配置しますか?

私の考えでは、シェルはregression/*既存ファイル名の明示的なリストに入れると、rmはその明示的なリストのファイルを削除しますが、run_regressionrmと同時に作成します。 run_regressionファイルにタイムスタンプを付けます。名前の衝突があってはなりません。

ただし、シェルがファイルの一覧表示を完了し、rmが機能し始めた時期を確認する方法がよくわかりません。上記の10秒で十分ですか? bashでこのようなことをすることはできますか?

% rm -rf regression/* & ( wait_unil_names_are_resolved ; run_regression )

コメントごとに、ツールを呼び出す前に、シェルに親密に知られているツールであっても、ワイルドカードがファイル名に展開されることをシェルが保証するかどうかを実際に尋ねていることを明確にしています。シェルとツールの両方の開発者が、ツールを使用してワイルドカード拡張をパイプライン化したくなるかもしれないと想像できます。それを防ぐ基準があるといいのですが。

5
Michael

コマンドはおそらく機能しますが、テストケースは次のとおりです。

$ ls
$ echo * $(sleep 1)&touch file1
[1] 12798
$ file1

[1]+  Done                    echo * $(sleep 1)

File1は入力されておらず、echoコマンドの出力であることに注意してください。

編集:

別のテスト実行:

$ ls
$ touch file1
$ for i in {1..5000}; do rm * & touch file$i; wait;done|grep file
rm: cannot remove '*': No such file or directory
***previous line repeated 14 times***
5
adonis

rm -rf regression/*並列( sleep 10 ; run_regression )で実行されます。これは、物事の順序について保証がないことを意味します。 rm -rf regression/*は最初にregressionディレクトリ内のファイルのリストを収集し、次にrmを呼び出してそれらを削除します。これは魔法では起こりません。コマンドrm -rf regression/*の評価の一部として作業を行うのはシェルであり、これは&演算子によって引き起こされたフォークの後に発生します。収集ステップに10秒未満かかる場合、run_regressionによって作成されたファイルは安全です。収集ステップがrun_regressionによって作成されたファイルに到達するのに10秒以上かかる場合、そのファイルは削除されます。

ファイルを閉じて再度開かない限り、ファイルを削除しても実際にはrun_regressionには影響しません。ファイルを削除しても、ファイルを開いているプロセスには影響しません。ファイルを開いているすべてのプロセスがファイルを閉じるまで、ファイルはディレクトリエントリなし(つまり、ハードリンクカウント0)で存在し続けます。ただし、プログラムの出力は削除されるため、プログラムの出力にアクセスすることはできません。

したがって、これを行わないでください。タイミングに依存しないでください。10秒などの高い遅延で、テスト中に機能します(特に、ファイルが少ない、キャッシュがウォーム、I/Oピークがない、システムの一時停止がないなどの理由で)。あなたのテスト)、しかし遅かれ早かれそれは本番で失敗するでしょう。

本当にディレクトリを保持してその中のファイルを削除したい場合は、最初にファイル名の収集を行ってください。

files_to_delete=(regression/*)
rm -rf "${files_to_delete[@]}" & run_regression

(これは、配列を持つシェルを想定しています。プレーンshでは、set regression/*; rm -rf "$@" & run_regressionを使用します。)もちろん、これは、ファイルrun_regressionが存在しないファイルのみを作成することを前提としています。既存のファイルを上書きすると、それらのファイルは削除されます。

おそらく、このような複雑さはすべて必要ありません。実行するだけです。

rm -rf regression/*
run_regression

ファイルのリストが非常に大きくてキャッシュに収まらない場合、またはファイルシステムの書き込み操作が異常に遅い場合を除いて、名前のリストを収集する方が削除するよりも時間がかかるため、パフォーマンスに違いはありません。

削除操作のパフォーマンスが本当に悪い場合(これも異常です)、新しいディレクトリを作成します。

mv regression regression.old
mkdir regression
rm -rf regression.old &
run_regression
mv regression regression.old
rm -rf regression.old &
mkdir regression
run_regression

古い回帰ディレクトリの名前を変更し、バックグラウンドで削除し、新しい回帰ディレクトリを作成してから、プログラムを実行します。

run_regressionがディレクトリ自体を作成する場合、ディレクトリが存在しない場合は、3番目の手順は必要ありません。

regression.oldがすでに存在する場合のより安全なバージョンは、mktempを使用して、現在のディレクトリに一時ディレクトリを作成して使用することです。

td=$(mktemp -d -p .)
mv regression "$td/"
rm -rf "$td" &
unset td
mkdir regression
run_regression
1
cas

新しいファイル名を使用する場合にのみ安全です。シェルは、iノードなどではなくファイル名を認識しており、コマンドを実行する前にグロブ(ワイルドカードの拡張)を実行します。 [〜#〜] posix [〜#〜] によると:

2.6.6パス名の拡張

フィールド分割後、set -fが有効ではない場合、結果のコマンドラインの各フィールドは、 パターンマッチング表記 で説明されているアルゴリズムを使用して拡張され、 ファイル名拡張に使用されるパターン のルールで修飾されます。 。

つまり、実際にコマンドを実行する前に行われる解析の明確なステップです。 POSIXの複雑なケースのほとんどは、リダイレクト割り当てを扱います。この例には何もないので、これが当てはまります。

2.9.1簡単なコマンド

  1. 変数の割り当てまたはリダイレクトではない単語は展開されます。展開後にフィールドが残っている場合、最初のフィールドはコマンド名と見なされ、残りのフィールドはコマンドの引数になります。

質問に示されている例では、ディレクトリが削除されていないように見えます。削除された可能性のあるサブディレクトリの存在に依存している場合は、同じ警告が適用されます。

おそらく、タイムスタンプ(タイムスタンプのに対して10秒異なる)は、結果のファイル名の一部になります。

0
Thomas Dickey