1.
Il primo:
for f in *; do
echo "$f"
done
non riesce per file chiamati -n
, -e
e varianti come -nene
e con alcune implementazioni bash, con i nomi dei file che contiene barre rovesciate.
Il secondo:
find * -prune | while read f; do
echo "$f"
done
fallisce anche per più casi (file chiamati !
, -H
, -name
, (
, i nomi dei file che iniziare o terminare con spazi vuoti o contenere caratteri di nuova riga ...)
È la shell che si espande *
, find
non fa altro che stampare i file che riceve come argomenti. Avresti potuto anche usare printf '%s\n'
invece il quale printf
incorporato che eviterebbe anche il potenziale errore di troppi argomenti .
2.
L'espansione di *
è ordinata, puoi renderla un po 'più veloce se non hai bisogno dell'ordinamento. In zsh
:
for f (*(oN)) printf '%s\n' $f
o semplicemente:
printf '%s\n' *(oN)
bash
non ha equivalenti per quanto posso dire, quindi dovresti ricorrere a find
.
3.
find . ! -name . -prune ! -name '.*' -print0 |
while IFS= read -rd '' f; do
printf '%s\n' "$f"
done
(sopra usando -print0
un'estensione non standard GNU / BSD ).
Ciò comporta comunque la generazione di un comando find e l'uso di un while read
ciclo lento , quindi sarà probabilmente più lento rispetto all'utilizzo del for
ciclo a meno che l'elenco dei file non sia enorme.
4.
Inoltre, contrariamente all'espansione dei caratteri jolly della shell, find
eseguirà una lstat
chiamata di sistema su ciascun file, quindi è improbabile che il non ordinamento lo compensi.
Con GNU / BSD find
, questo può essere evitato usando la loro -maxdepth
estensione che attiverà un'ottimizzazione salvando lstat
:
find . -maxdepth 1 ! -name '.*' -print0 |
while IFS= read -rd '' f; do
printf '%s\n' "$f"
done
Perché find
inizia a produrre i nomi dei file non appena li trova (ad eccezione del buffering di output stdio), dove potrebbe essere più veloce se ciò che fai nel ciclo richiede molto tempo e l'elenco dei nomi dei file è più di un buffer stdio (4 / 8 kB). In tal caso, l'elaborazione all'interno del ciclo inizierà prima che find
abbia terminato di trovare tutti i file. Sui sistemi GNU e FreeBSD, puoi usare stdbuf
per far sì che ciò accada prima (disabilitando il buffering stdio).
5.
Il modo POSIX / standard / portatile per eseguire comandi per ciascun file find
è utilizzare il -exec
predicato:
find . ! -name . -prune ! -name '.*' -exec some-cmd {} ';'
Nel caso di echo
ciò, è meno efficiente che eseguire il looping nella shell poiché la shell avrà una versione integrata di echo
mentre find
dovrà generare un nuovo processo ed eseguirlo /bin/echo
per ogni file.
Se è necessario eseguire diversi comandi, è possibile eseguire:
find . ! -name . -prune ! -name '.*' -exec cmd1 {} ';' -exec cmd2 {} ';'
Ma attenzione che cmd2
viene eseguito solo se ha cmd1
esito positivo.
6.
Un modo canonico per eseguire comandi complessi per ogni file è chiamare una shell con -exec ... {} +
:
find . ! -name . -prune ! -name '.*' -exec sh -c '
for f do
cmd1 "$f"
cmd2 "$f"
done' sh {} +
Quella volta, siamo tornati ad essere efficienti echo
dal momento che stiamo usando sh
quello incorporato e la -exec +
versione viene generata il meno sh
possibile.
7.
Nei miei test su una directory con 200.000 file con nomi brevi su ext4, zsh
quello (paragrafo 2.) è di gran lunga il più veloce, seguito dal primo semplice for i in *
ciclo (anche se come al solito, bash
è molto più lento di altre shell per quello).
find
non apre i file che trova. L'unica cosa che posso vedere ti morde qui rispetto a un gran numero di file è ARG_MAX .