Existem várias maneiras viáveis de conseguir isso.
Se você quisesse manter sua versão original, isso poderia ser feito da seguinte maneira:
getlist() {
IFS=$'\n'
for file in $(find . -iname 'foo*') ; do
printf 'File found: %s\n' "$file"
done
}
Isso ainda falhará se os nomes dos arquivos tiverem novas linhas literais, mas os espaços não serão interrompidos.
No entanto, não é necessário mexer com o IFS. Aqui está minha maneira preferida de fazer isso:
getlist() {
while IFS= read -d $'\0' -r file ; do
printf 'File found: %s\n' "$file"
done < <(find . -iname 'foo*' -print0)
}
Se você achar a < <(command)
sintaxe desconhecida, leia sobre a substituição de processos . A vantagem disso for file in $(find ...)
é que arquivos com espaços, novas linhas e outros caracteres são manipulados corretamente. Isso funciona porque find
with -print0
usará um null
(aka \0
) como terminador para cada nome de arquivo e, diferentemente da nova linha, null não é um caractere legal em um nome de arquivo.
A vantagem disso em relação à versão quase equivalente
getlist() {
find . -iname 'foo*' -print0 | while read -d $'\0' -r file ; do
printf 'File found: %s\n' "$file"
done
}
É que qualquer atribuição de variável no corpo do loop while é preservada. Ou seja, se você canalizar while
como acima, o corpo do while
está em um subshell que pode não ser o que você deseja.
A vantagem da versão de substituição de processo find ... -print0 | xargs -0
é mínima: a xargs
versão é boa se tudo o que você precisa é imprimir uma linha ou executar uma única operação no arquivo, mas se você precisar executar várias etapas, a versão do loop será mais fácil.
EDIT : Aqui está um bom script de teste para que você possa ter uma idéia da diferença entre diferentes tentativas de resolver esse 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"