答えながら古い質問驚くべきことにfind
、以下の例では、ファイルを複数回処理することが可能なようです。
find dir -type f -name '*.txt' \
-exec sh -c 'mv "$1" "${1%.txt}_hello.txt"' sh {} ';'
またはより効率的
find dir -type f -name '*.txt' \
-exec sh -c 'for n; do mv "$n" "${n%.txt}_hello.txt"; done' sh {} +
このコマンドはファイルを探し、そのファイル.txt
名のサフィックスを 。.txt
_hello.txt
これにより、ディレクトリは*.txt
名前がパターンと一致する新しいファイルの蓄積を開始します_hello.txt
。
質問:実際に処理されないのはなぜですかfind
?私の経験ではそうではなく、私たちもそうしたくないからです。そうすると無限ループが発生するからです。ところでmv
代替の場合も同様です。cp
これPOSIX規格によると(私の強調)
検索中のディレクトリ階層からファイルが削除または追加された場合
find
検索にファイルを含めるかどうかは指定されていません。。
新しいファイルを含めるかどうかが指定されていないため、おそらくより安全なアプローチは次のとおりです。
find dir -type d -exec sh -c '
for n in "$1"/*.txt; do
test -f "$n" && mv "$n" "${n%.txt}_hello.txt"
done' sh {} ';'
ここではファイルではなくディレクトリを探しており、スクリプトfor
内のループはsh
最初の反復の前にその範囲を一度評価するため、同じ潜在的な問題は発生しません。
GNUfind
マニュアルはこれを明示的に明らかにせず、find
OpenBSDマニュアルも同様です。
答え1
find
ディレクトリを巡回しながら生成されたファイルを見つけることができますか?
つまり、そうです。しかし、実装によって異なります。すでに処理されているファイルは無視されるように条件を作成することをお勧めします。
言及したように、POSIXはどちらも保証しません。readdir()
基本的なシステムコールは保証されません。:
最後の呼び出し
opendir()
時以降にファイルが削除されるかディレクトリに追加された場合、後続の呼び出しがrewinddir()
そのファイルのエントリを返すかどうかreaddir()
は指定されません。
find
私のDebian(GNU検索、Debianパッケージバージョン)4.6.0+git+20161106-2
でテストしました。strace
タスクを実行する前にディレクトリ全体を読み取ることを示します。
ソースコードをもう少し調べると、GNU findはgnulibの一部を使ってディレクトリを読み取っているようです。gnulib/lib/fts.c(gl/lib/fts.c
圧縮find
パッケージ):
/* If possible (see max_entries, below), read no more than this many directory
entries at a time. Without this limit (i.e., when using non-NULL
fts_compar), processing a directory with 4,000,000 entries requires ~1GiB
of memory, and handling 64M entries would require 16GiB of memory. */
#ifndef FTS_MAX_READDIR_ENTRIES
# define FTS_MAX_READDIR_ENTRIES 100000
#endif
制限を100に変更して実行しました。
mkdir test; cd test; touch {0000..2999}.foo
find . -type f -exec sh -c 'mv "$1" "${1%.foo}.barbarbarbarbarbarbarbar"' sh {} \; -print
名前が5回変更されたこのファイルのような面白い結果が出ます。
1046. Baba Baba Baba Baba Baba Baba Baba Baba Baba Baba Baba Baba Baba Baba Baba Baba Baba Baba
明らかに、GNU findのデフォルトのビルドでこの効果を引き起こすには、非常に大きなディレクトリ(100,000を超えるエントリ)が必要ですが、キャッシュなしの単純なreaddir + processループはより脆弱です。
理論的には、この単純な実装は、オペレーティングシステムが常にファイルが返される順序で最後に名前が変更されたファイルを追加すると、無限readdir()
ループに陥ることがあります。
Linuxでは、readdir()
Cライブラリはgetdents()
すでに複数のディレクトリエントリを一度に返すシステムコールによって実装されています。これは、後で呼び出しがreaddir()
削除されたファイルを返すことができますが、非常に小さいディレクトリの場合は、起動状態のスナップショットを効果的に取得できることを意味します。他のシステムはわかりませんね。
上記のテストでは、ファイル名がその場所で上書きされるのを防ぐために、意図的に長いファイル名に名前を変更しました。とにかく、同じ長さの名前の変更に対する同じテストが、二重および三重の名前変更で行われた。もちろん、これが重要かどうかや方法は、ファイルシステムの内部構造によって異なります。
find
これらすべてを念頭に置いて表現することで、全体的な問題を避けることが賢明かもしれません。いいえ処理されたファイルと一致します。つまり、-name "*.foo"
私の例や! -name "*_hello.txt"
質問のコマンドに追加されました。