Bunu başarmanın birkaç uygulanabilir yolu vardır.
Orijinal sürümünüze yakından bağlı kalmak istiyorsanız, bu şekilde yapılabilir:
getlist() {
IFS=$'\n'
for file in $(find . -iname 'foo*') ; do
printf 'File found: %s\n' "$file"
done
}
Dosya adlarında gerçekte yeni satırlar varsa bu yine de başarısız olur, ancak boşluklar bunu kırmaz.
Ancak, IFS ile uğraşmak gerekli değildir. İşte bunu yapmak için tercih ettiğim yol:
getlist() {
while IFS= read -d $'\0' -r file ; do
printf 'File found: %s\n' "$file"
done < <(find . -iname 'foo*' -print0)
}
< <(command)Sözdizimini tanıdık bulmazsanız, işlem ikamesi hakkında bilgi okumalısınız . Bunun avantajı, for file in $(find ...)boşluklu, satırsonu ve diğer karakterlere sahip dosyaların doğru şekilde işlenmesidir. Bunun nedeni çalışır findile -print0bir kullanacaktır null(aka \0yeni satır aksine, her bir dosya adı için terminatör gibi) ve null bir dosya adında yasal bir karakter değildir.
Neredeyse eşdeğer versiyona göre bunun avantajı
getlist() {
find . -iname 'foo*' -print0 | while read -d $'\0' -r file ; do
printf 'File found: %s\n' "$file"
done
}
While döngüsünün gövdesinde herhangi bir değişken ataması korunur mu? Yani, whileyukarıdaki gibi boru yaparsanız , gövdesi whilebir alt kabuktadır ve bu da istediğiniz şey olmayabilir.
İşlem ikame sürümünün avantajı find ... -print0 | xargs -0asgari düzeydedir: xargsİhtiyacınız olan tek şey dosyaya bir satır yazdırmak veya tek bir işlem gerçekleştirmekse, ancak çok sayıda adım gerçekleştirmeniz gerekiyorsa döngü sürümü daha kolaysa, sürüm iyidir.
EDIT : İşte güzel bir test komut dosyası, böylece bu sorunu çözmek için farklı girişimler arasındaki fark hakkında bir fikir edinebilirsiniz
#!/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"