ディレクトリ内のファイルを再帰的に反復することは、次の方法で簡単に実行できます。
find . -type f -exec bar {} \;
ただし、上記は、条件付き分岐やループなどの多くを実行する必要がある、より複雑なものには機能しません。私はこれを上記に使用していました:
while read line; do [...]; done < <(find . -type f)
ただし、これは不明瞭な文字を含むファイルでは機能しないようです。
$ touch $'a\nb'
$ find . -type f
./a?b
そのようなあいまいな文字をうまく処理する代替手段はありますか?
safe 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' {} +
、少し速くなります。)
これにより、ループ内で標準入力を使用できるようになり、anyパスで機能します。
これは次のように簡単です。
find -exec sh -c 'inline script "$0"' {} \;
または...
find -exec executable_script {} \;
最も簡単な(しかし安全な)アプローチは、シェルグロビングを使用することです。
$ 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
は実際には正しいことに注意してください。
読み取りループを移植可能にするのは少し難しいですが、特にbashの場合は this のようなものを試すことができます。
関連部分:
while IFS= read -d $'\0' -r file ; do
printf 'File found: %s\n' "$file"
done < <(find . -iname 'foo*' -print0)
これは、find
に、NUL文字(0x00)で区切られた出力を印刷するように指示し、read
にNUL区切りの行(-d $'\0'
)バックスラッシュを他の文字のエスケープとして処理せずに(-r
)行でワード分割を行わない(IFS=
)。 0x00は、Unixのファイル名またはパスでは発生しないバイトであるため、これはすべての奇妙なファイル名の問題を処理するはずです。