出力はどのくらい安全ですか?rmと同時に/*

出力はどのくらい安全ですか?rmと同時に/*

場合によっては、ディレクトリのすべての内容を削除し、そこに新しいファイルを作成する必要があります。次の操作を実行し、すべての新しいファイルが変更されていないままであることを期待できます。

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

run_regression一意の名前を持つように、出力ファイルにタイムスタンプをどこに追加してから配置しますかregression

私の考えでは、シェルはregression/*明示的なリストに解析されると思います。既存のfilenameを選択すると、その明示的なrmリスト内のファイルは削除されますが、run_regression.filenameと同時に作成された新しいファイルは削除されませんrm。そのファイルにはタイムスタンプが付けられているため、run_regression名前の競合はありません。

しかし、シェルがファイルのリストを完了し、作業を開始した時期をどのように知ることができるかはわかりませんrm。上記の10秒で十分ですか?次のようにできますかbash

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

説明を明確にするために、シェルに慣れているツールであっても、ツールを呼び出す前にワイルドカードがファイル名に拡張されることを保証するかどうかをシェルに実際に尋ねています。シェルとツール開発者がパイプワイルドカードを拡張するためにツールを使用したいと思うかもしれません。このようなことが起こらないようにする標準があってほしいです。

答え1

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

$ 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***

答え2

これは安全ではありません。

解決しようとしている問題が何であるかを指定していません。問題がディレクトリが常に存在するが、時々クリーンアップしたい場合は、確認されたファイルより古いファイルを明示的に削除することをお勧めします(sleep 1は私が厄介な部分です)。

touch regression.delete \
&& find regression \! -newer regression.delete -delete & \
&& sleep 1 \
&& run_regression

サブディレクトリがあると問題が発生します。代わりに書くことができます。

touch regression.delete \
&& find regression -mindepth 1 -maxdepth 1 \! -newer regression.delete -exec rm -rf '{}' \; & \
&& sleep 1 \
&& run_regression

問題ができるだけ早くプログラムを起動したい場合は、ディレクトリが一時的に存在せず、マウントポイントではない場合は、通常、次のコマンドを実行します。

mkdir regression.new \
&& chmod --reference regression regression.new \
&& mv regression regression.delete \
&& mv regression.new regression \
&& rm -rf regression.delete & \
run_regression

これにより、run_regressionをほぼすぐに開始できます。

編集内容に応答し(他の回答の研究に基づいて自分自身を編集)、rmコマンドを開始する前にワイルドカードを拡張する必要がありますが、問題の要点は、シェルフォーク後に拡張が実行されるかどうかを知ることです。非同期実行のためのPOSIX仕様私が知る限り、何らかの方法で明示的な仕様はありません。セクション2.1は間違いなく拡張が別の操作であり、コマンドの実際のフォーク/実行よりも先行しますが、テスト(@adonisによって、私はbash 4.3を使用して)を暗黙的に示します(コピー)。42(1)はbashが最も効率的アプローチを取ることを提案します。ワイルドカード拡張に時間がかかる場合は、次のコマンドで行った修正がその拡張に影響を与える可能性があります。したがって、最初に削除したくないファイルを削除しようと思うかもしれません。

bashのソースコードを見て、実行_cmd.c単語拡張の前にフォークが行われることを明確にしてください。

3922 | /* If we're in a pipeline or run in the background, set DOFORK so we
3923 |  make the child early, before word expansion.  This keeps assignment
3924 |  statements from affecting the parent shell's environment when they
3925 |  should not. */

答え3

rm -rf regression/*走る平行にそして( sleep 10 ; run_regression )。これは、物事の順序を保証できないことを意味します。rm -rf regression/*まず、ディレクトリ内のファイルのリストを収集regressionし、呼び出してrm削除します。これは真空中では発生せず、コマンドの評価中にシェルで実行される操作であり、rm -rf regression/*演算子によるフォークの後に発生します&。収集ステップが10秒未満の場合、生成されたファイルはrun_regression安全です。で作成したファイルに到達するために収集手順が10秒以上かかると、run_regressionそのファイルは削除されます。

ファイルを削除しても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

答え4

新しいファイル名を使用しても安全です。シェルは、inodeなどではなくファイル名を知っていて、コマンドを実行する前にワイルドカード(ワイルドカード拡張)を実行します。 ~によるとPOSIX:

2.6.6 パス名の拡張

フィールド分割後にset -f適用されない場合、生成されたコマンドラインの各フィールドは、次に説明するアルゴリズムを使用して拡張する必要があります。パターンマッチング表記、規則に従うファイル名拡張に使用されるパターン

これは、コマンドが実際に実行される前に発生する解析の明確なステップです。 POSIX で最も複雑なケースが処理されます。リダイレクトそして仕事。この例には何もないので、適用される内容は次のとおりです。

2.9.1 簡単なコマンド

  1. 変数の割り当てやリダイレクト以外の単語は拡張する必要があります。拡張後に残りのフィールドがある場合、最初のフィールドはコマンド名として扱われ、残りのフィールドはコマンドの引数です。

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

おそらくあなたのタイムスタンプ(10秒はい違いは第二タイムスタンプ)は結果ファイル名の一部になります。

関連情報