ディレクトリ内のファイルを再帰的に反復処理する

ディレクトリ内のファイルを再帰的に反復処理する

ディレクトリ内のファイルを再帰的に反復処理するには、次のようにします。

find . -type f -exec bar {} \;

ただし、上記の方法は、条件分岐やループ処理などを多く行う必要がある複雑な処理には適していません。私は上記の処理に次のように使用していました。

while read line; do [...]; done < <(find . -type f)

ただし、わかりにくい文字を含むファイルではこの方法は機能しないようです。

$ touch $'a\nb'
$ find . -type f
./a?b

このようなわかりにくい文字をうまく処理できる代​​替手段はありますか?

答え1

さらに別の用途安全find:

while IFS= read -r -d '' -u 9
do
    [Do something with "$REPLY"]
done 9< <( find . -type f -exec printf '%s\0' {} + )

(これはどの POSIX でも動作しますfindが、シェル部分には bash が必要です。 *BSD と GNU find では、-print0の代わりにを使用する-exec printf '%s\0' {} +と、若干高速になります。)

これにより、ループ内で標準入力を使用することが可能になり、どれでもパス。

答え2

これを行うのは簡単です:

find -exec sh -c 'inline script "$0"' {} \;

または...

find -exec executable_script {} \;

答え3

最も簡単な(しかし安全な)アプローチは、シェル グロブを使用することです。

$ for f in *; do printf ":%s:\n" "$f"; done 
:a b:
:c
d:
:-e:
:e  f:
h:

上記をサブディレクトリに再帰的に実行するには (bash の場合)、globstarオプションを使用します。また、dotglob名前が で始まるファイルに一致するように設定することもできます.

$ shopt -s globstar dotglob
$ for f in **/*; do printf ":%s:\n" "$f"; done 
:a b:
:c
d:
:-e:
:e  f:
:foo:
:foo/file1:
:foo/file two:
h:

bash 4.2 までは、**/ディレクトリへのシンボリック リンクに再帰することに注意してください。bash 4.3 以降では、**/のように、ディレクトリにのみ再帰しますfind

find -print0もう一つの一般的な解決策は、以下を使用することですxargs -0:

$ touch -- 'a b' $'c\nd' $'e\tf' $'g\rh' '-e'
$ find . -type f -print0 | xargs -0 -I{} printf ":%s:\n" {}
h:/g
:./e    f:
:./a b:
:./-e:
:./c
d:

h:/gファイル名に が含まれているため、は実際には正しいことに注意してください\r

答え4

読み取りループを移植可能にするのは少し難しいですが、特にbashの場合は次のようなものを試すことができます。これ

該当部分:

while IFS= read -d $'\0' -r file ; do
        printf 'File found: %s\n' "$file"
done < <(find . -iname 'foo*' -print0)

これは、find出力を NUL 文字 (0x00) で区切って印刷し、バックスラッシュを他の文字のエスケープとして処理せずにreadNUL で区切られた行 ( ) を取得し、行の単語分割を行わないように指示します ( )。0x00 は Unix のファイル名やパスには出現できないバイトであるため、これにより奇妙なファイル名の問題はすべて処理されるはずです。-d $'\0'-rIFS=

関連情報