Il y a plusieurs réalisable façons de le faire.
Si vous voulais rester au plus près de votre version d'origine, il pourrait être fait de cette façon:
getlist() {
IFS=$'\n'
for file in $(find . -iname 'foo*') ; do
printf 'File found: %s\n' "$file"
done
}
Cela va encore échouer si les noms de fichier ont littérale des retours à la ligne dans les, mais les espaces ne pas le casser.
Cependant, de jouer avec les FI n'est pas nécessaire. Voici ma façon préférée de faire ceci:
getlist() {
while IFS= read -d $'\0' -r file ; do
printf 'File found: %s\n' "$file"
done < <(find . -iname 'foo*' -print0)
}
Si vous trouvez l' < <(command)
de la syntaxe inconnu vous devriez lire à propos du processus de substitution. L'avantage de cette de plus de for file in $(find ...)
, c'est que des fichiers avec des espaces, retours à la ligne et d'autres caractères sont traités correctement. Cela fonctionne parce qu' find
avec -print0
va utiliser un null
(aka \0
), comme le terminateur de chaque nom de fichier et, à la différence de saut de ligne, la valeur null n'est pas un caractère dans un nom de fichier.
L'avantage de ce système au cours de la presque l'équivalent de la version
getlist() {
find . -iname 'foo*' -print0 | while read -d $'\0' -r file ; do
printf 'File found: %s\n' "$file"
done
}
C'est que toute affectation de variable dans le corps de la boucle while est préservée. C'est, si vous pipe while
comme ci-dessus puis le corps de l' while
est dans un shell interne est exécuté, qui peut ne pas être ce que vous voulez.
L'avantage de ce processus de substitution version plus find ... -print0 | xargs -0
est minime: L' xargs
version est très bien si vous avez besoin d'imprimer une ligne ou d'effectuer une opération sur le fichier, mais si vous devez effectuer plusieurs étapes de la boucle version est plus facile.
EDIT: Voici un beau script de test de sorte que vous pouvez obtenir une idée de la différence entre les différentes tentatives de résolution de ce problème
#!/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"