Es gibt verschiedene praktikable Möglichkeiten, um dies zu erreichen.
Wenn Sie sich eng an Ihre Originalversion halten möchten, können Sie dies folgendermaßen tun:
getlist() {
IFS=$'\n'
for file in $(find . -iname 'foo*') ; do
printf 'File found: %s\n' "$file"
done
}
Dies schlägt immer noch fehl, wenn Dateinamen wörtliche Zeilenumbrüche enthalten, Leerzeichen diese jedoch nicht beschädigen.
Es ist jedoch nicht erforderlich, mit IFS zu spielen. Hier ist meine bevorzugte Methode, um dies zu tun:
getlist() {
while IFS= read -d $'\0' -r file ; do
printf 'File found: %s\n' "$file"
done < <(find . -iname 'foo*' -print0)
}
Wenn Sie die < <(command)
Syntax nicht kennen, sollten Sie sich über die Prozessersetzung informieren . Dies hat den Vorteil, for file in $(find ...)
dass Dateien mit Leerzeichen, Zeilenumbrüchen und anderen Zeichen korrekt behandelt werden. Dies funktioniert, weil find
with -print0
ein null
(aka \0
) als Abschlusszeichen für jeden Dateinamen verwendet und im Gegensatz zu newline null kein zulässiges Zeichen in einem Dateinamen ist.
Der Vorteil gegenüber der nahezu gleichwertigen Version
getlist() {
find . -iname 'foo*' -print0 | while read -d $'\0' -r file ; do
printf 'File found: %s\n' "$file"
done
}
Ist das eine Variablenzuweisung im Körper der while-Schleife erhalten bleibt. Das heißt, wenn Sie while
wie oben while
pfeifen, befindet sich der Körper des in einer Unterschale, die möglicherweise nicht das ist, was Sie wollen.
Der Vorteil der Prozessersetzungsversion gegenüber der Version find ... -print0 | xargs -0
ist minimal: Die xargs
Version ist in Ordnung, wenn Sie lediglich eine Zeile drucken oder einen einzelnen Vorgang für die Datei ausführen müssen. Wenn Sie jedoch mehrere Schritte ausführen müssen, ist die Schleifenversion einfacher.
BEARBEITEN : Hier ist ein schönes Testskript, damit Sie sich ein Bild über den Unterschied zwischen verschiedenen Versuchen machen können, dieses Problem zu lösen
#!/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"