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 .