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 find
ile -print0
bir kullanacaktır null
(aka \0
yeni 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, while
yukarıdaki gibi boru yaparsanız , gövdesi while
bir alt kabuktadır ve bu da istediğiniz şey olmayabilir.
İşlem ikame sürümünün avantajı find ... -print0 | xargs -0
asgari 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"