Questa domanda è ispirata da
Perché l'utilizzo di un ciclo di shell per elaborare il testo è considerato una cattiva pratica?
Vedo questi costrutti
for file in `find . -type f -name ...`; do smth with ${file}; done
e
for dir in $(find . -type d -name ...); do smth with ${dir}; done
utilizzato qui quasi quotidianamente anche se alcune persone si prendono il tempo di commentare quei post spiegando perché questo genere di cose dovrebbe essere evitato...
Vedere il numero di tali post (e il fatto che a volte quei commenti sono semplicemente ignorato) ho pensato che avrei potuto anche fare una domanda:
Perché sta scorrendo su find
's output di cattiva pratica e qual è il modo corretto di eseguire uno o più comandi per ogni nome file/percorso restituito da find
?
Risposta accettata:
Il problema
for f in $(find .)
combina due cose incompatibili.
find
stampa un elenco di percorsi di file delimitati da caratteri di nuova riga. Mentre l'operatore split+glob che viene invocato quando lasci quel $(find .)
non quotato in quel contesto di elenco lo divide sui caratteri di $IFS
(per impostazione predefinita include newline, ma anche spazio e tab (e NUL in zsh
)) ed esegue il globbing su ogni parola risultante (tranne in zsh
) (e persino l'espansione delle parentesi nei derivati ksh93 o pdksh!).
Anche se ce la fai:
IFS='
' # split on newline only
set -o noglob # disable glob (also disables brace expansion in pdksh
# but not ksh93)
for f in $(find .) # invoke split+glob
È ancora sbagliato poiché il carattere di nuova riga è valido come qualsiasi altro in un percorso di file. L'output di find -print
semplicemente non è post-elaborabile in modo affidabile (tranne usando qualche trucco contorto, come mostrato qui ).
Ciò significa anche che la shell deve memorizzare l'output di find
completamente, quindi suddividilo + glob (il che implica la memorizzazione dell'output una seconda volta in memoria) prima di iniziare a scorrere i file.
Nota che find . | xargs cmd
ha problemi simili (là, spazi vuoti, nuova riga, virgolette singole, virgolette doppie e barra rovesciata (e con alcuni xarg
byte di implementazione che non fanno parte di caratteri validi) sono un problema)
Alternative più corrette
L'unico modo per usare un for
loop sull'output di find
sarebbe usare zsh
che supporta IFS=$'