GNU/Linux >> Linux Esercitazione >  >> Linux

Perché il loop over dell'output di Find è una cattiva pratica?

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=$'

Linux
  1. Ssh:perché Firefox è così lento rispetto a Ssh?

  2. Iterazione su ogni riga dell'output di ls -l

  3. Itera su un elenco di file con spazi

  4. Output del comando Linux come parametro di un altro comando

  5. sudoedit:perché usarlo su sudo vi?

Perché "meno" non mostra output in grassetto??

Come eseguire il looping delle directory in Linux?

Perché questa pipeline di shell termina?

Linux perché non riesco a reindirizzare find result a rm?

Perché è così difficile trovare un file in Ubuntu?

Sudo su è considerata una cattiva pratica?