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

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

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

Однако вышеизложенное не работает для более сложных вещей, где требуется множество условных ветвей, циклов и т. д. Я использовал это для вышесказанного:

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

Однако, похоже, что это не работает для файлов, содержащих неясные символы:

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

Есть ли альтернатива, которая хорошо справляется с такими неясными символами?

14 голосов | спросил user2064000 26 J0000006Europe/Moscow 2014, 17:52:17

4 ответа


5

Еще одно использование для безопасного find :

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

(Это работает с любым POSIX find, но для части оболочки требуется bash. С помощью * BSD и GNU find вы можете использовать ---- +: = 3 =: + ---- вместо -print0, это будет немного быстрее.)

Это позволяет использовать стандартный ввод в цикле и работает с любым контуром.

ответил l0b0 26 J0000006Europe/Moscow 2014, 19:51:25
9

Выполнение этого так же просто, как:

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

Или ...

find -exec executable_script {} \;
ответил mikeserv 26 J0000006Europe/Moscow 2014, 18:08:23
5

Немного сложно сделать ваш цикл чтения портативно, но для bash, в частности, вы можете попробовать что-то вроде это .

Релевантная часть:

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

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

ответил godlygeek 26 J0000006Europe/Moscow 2014, 18:04:36
5

Самый простой (но безопасный) подход заключается в использовании globbing оболочки:

$ 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.

ответил terdon 26 J0000006Europe/Moscow 2014, 18:18:53

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132