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 .