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 .