Esistono diversi modi praticabili per ottenere questo risultato.
Se si desidera attenersi strettamente alla versione originale, è possibile farlo in questo modo:
getlist() {
IFS=$'\n'
for file in $(find . -iname 'foo*') ; do
printf 'File found: %s\n' "$file"
done
}
Ciò continuerà a fallire se i nomi dei file contengono letteralmente nuove righe, ma gli spazi non lo spezzeranno.
Tuttavia, non è necessario scherzare con IFS. Ecco il mio modo preferito per farlo:
getlist() {
while IFS= read -d $'\0' -r file ; do
printf 'File found: %s\n' "$file"
done < <(find . -iname 'foo*' -print0)
}
Se trovi la < <(command)
sintassi sconosciuta, dovresti leggere sulla sostituzione del processo . Il vantaggio di questo for file in $(find ...)
è che i file con spazi, newline e altri caratteri vengono gestiti correttamente. Questo funziona perché find
con -print0
verrà utilizzato un null
(aka\0
) come terminatore per ogni nome di file e, a differenza di newline, null non è un carattere legale in un nome di file.
Il vantaggio di questo rispetto alla versione quasi equivalente
getlist() {
find . -iname 'foo*' -print0 | while read -d $'\0' -r file ; do
printf 'File found: %s\n' "$file"
done
}
È che qualsiasi assegnazione variabile nel corpo del ciclo while sia preservata. Cioè, se si esegue il pipe while
come sopra, quindi il corpo diwhile
è in una subshell che potrebbe non essere quello che si desidera.
Il vantaggio della versione di sostituzione del processo find ... -print0 | xargs -0
è minimo: la xargs
versione va bene se tutto ciò che serve è stampare una riga o eseguire una singola operazione sul file, ma se è necessario eseguire più passaggi la versione del ciclo è più semplice.
EDIT : Ecco un bel script di test in modo da poter farti un'idea della differenza tra diversi tentativi di risolvere questo problema
#!/usr/bin/env bash
dir=/tmp/getlist.test/
mkdir -p "$dir"
cd "$dir"
touch 'file not starting foo' foo foobar barfoo 'foo with spaces'\
'foo with'$'\n'newline 'foo with trailing whitespace '
# while with process substitution, null terminated, empty IFS
getlist0() {
while IFS= read -d $'\0' -r file ; do
printf 'File found: '"'%s'"'\n' "$file"
done < <(find . -iname 'foo*' -print0)
}
# while with process substitution, null terminated, default IFS
getlist1() {
while read -d $'\0' -r file ; do
printf 'File found: '"'%s'"'\n' "$file"
done < <(find . -iname 'foo*' -print0)
}
# pipe to while, newline terminated
getlist2() {
find . -iname 'foo*' | while read -r file ; do
printf 'File found: '"'%s'"'\n' "$file"
done
}
# pipe to while, null terminated
getlist3() {
find . -iname 'foo*' -print0 | while read -d $'\0' -r file ; do
printf 'File found: '"'%s'"'\n' "$file"
done
}
# for loop over subshell results, newline terminated, default IFS
getlist4() {
for file in "$(find . -iname 'foo*')" ; do
printf 'File found: '"'%s'"'\n' "$file"
done
}
# for loop over subshell results, newline terminated, newline IFS
getlist5() {
IFS=$'\n'
for file in $(find . -iname 'foo*') ; do
printf 'File found: '"'%s'"'\n' "$file"
done
}
# see how they run
for n in {0..5} ; do
printf '\n\ngetlist%d:\n' $n
eval getlist$n
done
rm -rf "$dir"