「for f in *」sh ループの途中でファイルが追加/削除されるとどうなりますか?

「for f in *」sh ループの途中でファイルが追加/削除されるとどうなりますか?

オンラインでforループの例を見つけました。今、それを自分のコードで使いたいのですが、このループがどのように動作するのかよく分かりません。

for entry in "$search_dir"/* 
do
  echo "$entry"
done

今私はこう尋ねたい

  1. 各反復で search_dir を検索し、各反復で search_dir 内のファイルを 1 つのファイルずつエントリ変数にコピーしますか?
  2. それとも、search_dir のすべてのコンテンツのスナップショットを取得し、そのスナップショットをエントリ変数に保存しますか?
  3. ループがまだ動作しているときに誰かが search_dir にファイルを挿入した場合、出力に何か変化はありますか?

答え1

シェルがfor- ステートメントに到達すると、 の値を展開し$search_dir、ファイル名のグロビングを実行して、反復処理されるディレクトリ エントリのリストを生成します。これは 1 回だけ実行され、 内のものが$search_dir消えたり、ループの実行中にそのディレクトリに新しいファイル/ディレクトリが追加されたりしても、これらの変更は取得されません。

ループが にある名前のディレクトリ エントリに対して動作する場合$entry、特にループの実行に長い時間がかかることがわかっていて、何らかの理由で常に変化するファイルが多数ある場合は、ループ内でそれらのエントリが存在するかどうかをテストする必要があります。

for entry in "$search_dir"/*; do
    if [ -e "$entry" ]; then
        # operate on "$entry"
    else
        # handle the case that "$entry" went away
    fi
done

ステファンがコメントで正しく指摘しているように、これはほとんどケース。

答え2

シェルは、ループ本体の実行を開始する前に、ループする値のリストを完全に決定します。つまり、次のようになります。

  1. シェルは変数の値を使用してパスを構築しますsearch_dir
  2. シェルは指定されたディレクトリ内のファイル名のリストを収集し、ワイルドカード パターンに一致するリストを構築します。
  3. シェルは、一致リストの各要素に対して、ループ本体を順番に実行します。

ループの実行中に、変数の値を変更したりsearch_dir、ディレクトリの内容を変更できます。これは、ループがどのファイルに対して作用するかには影響しません。

ループが他のファイルを処理している間にファイルが削除された場合、そのファイルに到達したときには、そのファイルは存在しません。ループで何を行うかによって、問題になる場合とならない場合があります。ファイルを削除する可能性のある同時プロセスがある場合、処理前にファイルが存在するかどうかをテストしても、実際にはその問題は解決されないことに注意してください。テストしてから処理を開始するまでの間にファイルが削除される可能性があります

ファイルを処理済みとしてマークして、2度処理されないようにする必要がある場合は、このスクリプトでファイルを処理したら別のディレクトリに移動する必要があります。ファイルを別のディレクトリ(同じファイルシステム上)に移動することは、原子: まだ終わっていないか、すでに終わっているかのどちらかであり、中間の状態はありません。しかし、もう一度言いますが、違うプロセス (おそらくそのスクリプトの別のインスタンス) がファイルを移動すると、ループの実行中に移動されたファイルがループにヒットすることがあります。

新しいファイルが作成されたときにそれを処理したい場合は、再度ループする必要があります。明らかに、ループの実行中に、または以前のファイルがすべて処理された後にファイルが作成される可能性があるため、スクリプトは永久に実行し続ける必要があります。ディレクトリにファイルが作成されるまで待機するツールがあります。Linuxでは、そのための基本的な機能は次のとおりです。通知; 作成されたファイルを処理する必要がある場合は、inotifywaitまたはインクロン役に立つはずです。inotify は、作成されたファイル(またはトリガーに応じて変更またはアクセスされたファイル)のみを通知することを覚えておいてください。inotify ベースのコマンドが起動します。また、既存のファイルも処理する必要がありますが、for entry in *; do …; done; inotifywait …ループの実行中やコマンドの起動中にファイルが作成される可能性があるため、単純に処理することはinotifywaitできません。

関連情報