1.
Il primo:
for f in *; do
echo "$f"
done
non riesce per file chiamati -n, -ee varianti come -nenee 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 *, findnon fa altro che stampare i file che riceve come argomenti. Avresti potuto anche usare printf '%s\n'invece il quale printfincorporato 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)
bashnon 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 -print0un'estensione non standard GNU / BSD ).
Ciò comporta comunque la generazione di un comando find e l'uso di un while readciclo lento , quindi sarà probabilmente più lento rispetto all'utilizzo del forciclo a meno che l'elenco dei file non sia enorme.
4.
Inoltre, contrariamente all'espansione dei caratteri jolly della shell, findeseguirà una lstatchiamata di sistema su ciascun file, quindi è improbabile che il non ordinamento lo compensi.
Con GNU / BSD find, questo può essere evitato usando la loro -maxdepthestensione che attiverà un'ottimizzazione salvando lstat:
find . -maxdepth 1 ! -name '.*' -print0 |
while IFS= read -rd '' f; do
printf '%s\n' "$f"
done
Perché findinizia 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 findabbia terminato di trovare tutti i file. Sui sistemi GNU e FreeBSD, puoi usare stdbufper 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 -execpredicato:
find . ! -name . -prune ! -name '.*' -exec some-cmd {} ';'
Nel caso di echociò, è meno efficiente che eseguire il looping nella shell poiché la shell avrà una versione integrata di echomentre finddovrà generare un nuovo processo ed eseguirlo /bin/echoper ogni file.
Se è necessario eseguire diversi comandi, è possibile eseguire:
find . ! -name . -prune ! -name '.*' -exec cmd1 {} ';' -exec cmd2 {} ';'
Ma attenzione che cmd2viene eseguito solo se ha cmd1esito 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 echodal momento che stiamo usando shquello incorporato e la -exec +versione viene generata il meno shpossibile.
7.
Nei miei test su una directory con 200.000 file con nomi brevi su ext4, zshquello (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).
findnon apre i file che trova. L'unica cosa che posso vedere ti morde qui rispetto a un gran numero di file è ARG_MAX .