1.
Первый:
for f in *; do
echo "$f"
done
происходит сбой для вызываемых файлов -n, -eи вариантов, таких как -neneи с некоторыми развертываниями bash, с именами файлов, содержащими обратную косую черту.
Секунда:
find * -prune | while read f; do
echo "$f"
done
не может даже больше случаев (файлы называются !, -H, -name, (, имена файлов , которые начинаются или конец с пробелами или содержат символы новой строки ...)
Это оболочка, которая расширяется *, findничего не делает, кроме печати файлов, которые она получает в качестве аргументов. Вы могли бы также использовать printf '%s\n'вместо этого printfвстроенную функцию, которая также избежала бы слишком большой потенциальной ошибки аргументов .
2.
Расширение *отсортировано, вы можете сделать его немного быстрее, если вам не нужна сортировка. В zsh:
for f (*(oN)) printf '%s\n' $f
или просто:
printf '%s\n' *(oN)
bashнасколько я могу судить, не имеет аналогов, поэтому вам придется прибегнуть к помощи find.
3.
find . ! -name . -prune ! -name '.*' -print0 |
while IFS= read -rd '' f; do
printf '%s\n' "$f"
done
(выше с использованием -print0нестандартного расширения GNU / BSD ).
Это все еще включает в себя создание команды find и использование медленного while readцикла, поэтому, вероятно, он будет медленнее, чем использование forцикла, если список файлов не будет огромным.
4.
Кроме того, в отличие от расширения с подстановочными символами оболочки, системный вызов findбудет выполняться для lstatкаждого файла, поэтому маловероятно, что несортировка компенсирует это.
С GNU / BSD findэтого можно избежать, используя их -maxdepthрасширение, которое вызовет оптимизацию, сохраняя lstat:
find . -maxdepth 1 ! -name '.*' -print0 |
while IFS= read -rd '' f; do
printf '%s\n' "$f"
done
Потому что findначинает выводить имена файлов, как только находит их (за исключением буферизации вывода stdio), где это может быть быстрее, если то, что вы делаете в цикле, занимает много времени, а список имен файлов больше, чем буфер stdio (4 / 8 кБ). В этом случае обработка внутри цикла начнется до того, как findбудет завершен поиск всех файлов. В системах GNU и FreeBSD вы можете использовать, stdbufчтобы это произошло раньше (отключение буферизации stdio).
5.
POSIX / стандартный / переносимый способ запуска команд для каждого файла с findиспользованием -execпредиката:
find . ! -name . -prune ! -name '.*' -exec some-cmd {} ';'
В случае, echoоднако, это менее эффективно, чем выполнение цикла в оболочке, поскольку оболочка будет иметь встроенную версию, в echoто время как findпотребуется порождать новый процесс и выполнять /bin/echoв нем для каждого файла.
Если вам нужно выполнить несколько команд, вы можете сделать:
find . ! -name . -prune ! -name '.*' -exec cmd1 {} ';' -exec cmd2 {} ';'
Но будьте осторожны, cmd2это выполняется только в случае cmd1успеха.
6.
Канонический способ запуска сложных команд для каждого файла - вызвать оболочку с помощью -exec ... {} +:
find . ! -name . -prune ! -name '.*' -exec sh -c '
for f do
cmd1 "$f"
cmd2 "$f"
done' sh {} +
На этот раз мы вернулись к эффективности, echoпоскольку мы используем shвстроенную -exec +версию, а версия порождает shкак можно меньше.
7.
В моих тестах с каталогом, содержащим 200 000 файлов с короткими именами в ext4, zshодин (параграф 2.) является самым быстрым, за ним следует первый простой for i in *цикл (хотя, как обычно, bashон намного медленнее, чем другие оболочки).
findне открывает файлы, которые он находит. Единственное, что я вижу, кусает вас здесь из-за большого количества файлов - это ARG_MAX .