1.
Pierwszy:
for f in *; do
echo "$f"
done
nie plików o nazwie -n
, -e
i warianty jak -nene
iz niektórych wdrożeniach bash, ale w nazwach zawierających backslashy.
Drugi:
find * -prune | while read f; do
echo "$f"
done
nie dla jeszcze większej liczby przypadków (plików o nazwie !
, -H
, -name
, (
, nazw plików, które rozpoczynają lub kończą się puste lub zawierają znaki nowej linii ...)
To powłoka, która się rozszerza *
, find
nic nie robi, tylko drukuje pliki, które otrzymuje jako argumenty. Równie dobrze możesz użyć printf '%s\n'
zamiast tego, który printf
jest wbudowany, aby uniknąć zbyt wielu potencjalnych błędów args .
2)
Rozszerzenie *
jest posortowane, możesz je przyspieszyć, jeśli nie potrzebujesz sortowania. W zsh
:
for f (*(oN)) printf '%s\n' $f
lub po prostu:
printf '%s\n' *(oN)
bash
o ile mi wiadomo, nie ma odpowiednika, więc musisz się do niego odwołać find
.
3)
find . ! -name . -prune ! -name '.*' -print0 |
while IFS= read -rd '' f; do
printf '%s\n' "$f"
done
(powyżej przy użyciu -print0
niestandardowego rozszerzenia GNU / BSD ).
To wciąż wymaga odrodzenia polecenia find i użycia wolnej while read
pętli, więc prawdopodobnie będzie wolniejsza niż użycie for
pętli, chyba że lista plików jest ogromna.
4
Ponadto, w przeciwieństwie do rozszerzenia symboli wieloznacznych powłoki, find
wykona lstat
wywołanie systemowe dla każdego pliku, więc jest mało prawdopodobne, aby brak sortowania to zrekompensował.
W przypadku GNU / BSD find
można tego uniknąć, stosując ich -maxdepth
rozszerzenie, które uruchomi optymalizację oszczędzając lstat
:
find . -maxdepth 1 ! -name '.*' -print0 |
while IFS= read -rd '' f; do
printf '%s\n' "$f"
done
Ponieważ find
zaczyna wypisywać nazwy plików, gdy tylko je znajdzie (z wyjątkiem buforowania wyjścia stdio), dlatego może być szybsze, jeśli to, co robisz w pętli, jest czasochłonne, a lista nazw plików to więcej niż bufor stdio (4 / 8 kB). W takim przypadku przetwarzanie w pętli rozpocznie się przed find
zakończeniem wyszukiwania wszystkich plików. W systemach GNU i FreeBSD możesz użyć tego, stdbuf
aby spowodować to wcześniej (wyłączenie buforowania stdio).
5
POSIX / standard / przenośny sposób uruchamiania poleceń dla każdego pliku za find
pomocą -exec
predykatu:
find . ! -name . -prune ! -name '.*' -exec some-cmd {} ';'
W tym przypadku jest to echo
jednak mniej wydajne niż wykonywanie pętli w powłoce, ponieważ powłoka będzie miała wbudowaną wersję, echo
podczas gdy find
będzie wymagać odrodzenia nowego procesu i wykonania /bin/echo
w nim dla każdego pliku.
Jeśli chcesz uruchomić kilka poleceń, możesz:
find . ! -name . -prune ! -name '.*' -exec cmd1 {} ';' -exec cmd2 {} ';'
Ale uważaj, że cmd2
jest wykonywany tylko wtedy, gdy cmd1
się powiedzie.
6.
Kanonicznym sposobem uruchamiania złożonych poleceń dla każdego pliku jest wywołanie powłoki za pomocą -exec ... {} +
:
find . ! -name . -prune ! -name '.*' -exec sh -c '
for f do
cmd1 "$f"
cmd2 "$f"
done' sh {} +
Tym razem wracamy do sprawności, echo
ponieważ używamy sh
wbudowanego, a -exec +
wersja odradza się sh
jak najmniej.
7
W moich testach katalogu z 200 000 plików o krótkich nazwach na ext4 ten zsh
(paragraf 2) jest zdecydowanie najszybszy, a następnie pierwsza prosta for i in *
pętla (choć jak zwykle bash
jest znacznie wolniejsza niż inne powłoki).
find
nie otwiera znalezionych plików. Jedyne, co widzę, gryząc cię tutaj w odniesieniu do dużej liczby plików, to ARG_MAX .