Рекурсивно перебирать файлы в каталоге

Рекурсивно перебирать файлы в каталоге

Рекурсивный перебор файлов в каталоге можно легко выполнить следующим образом:

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), и readизвлекать строки, разделенные символами NUL ( -d $'\0'), не обрабатывая обратные косые черты как экранирования других символов ( -r) и не выполняя никаких разбиений слов в строках ( IFS=). Поскольку 0x00 — это байт, который не может встречаться в именах файлов или путях в Unix, это должно решить все ваши странные проблемы с именами файлов.

Связанный контент