Ada beberapa cara yang bisa dilakukan untuk mencapai ini.
Jika Anda ingin tetap menggunakan versi asli Anda, itu bisa dilakukan dengan cara ini:
getlist() {
IFS=$'\n'
for file in $(find . -iname 'foo*') ; do
printf 'File found: %s\n' "$file"
done
}
Ini masih akan gagal jika nama file memiliki baris baru literal di dalamnya, tetapi spasi tidak akan merusaknya.
Namun, mengacaukan IFS tidak perlu. Inilah cara pilihan saya untuk melakukan ini:
getlist() {
while IFS= read -d $'\0' -r file ; do
printf 'File found: %s\n' "$file"
done < <(find . -iname 'foo*' -print0)
}
Jika Anda menemukan < <(command)sintaksis tidak dikenal, Anda harus membaca tentang substitusi proses . Keuntungan dari ini for file in $(find ...)adalah bahwa file dengan spasi, baris baru dan karakter lainnya ditangani dengan benar. Ini berfungsi karena finddengan -print0akan menggunakan null(alias \0) sebagai terminator untuk setiap nama file dan, tidak seperti baris baru, null bukan karakter hukum dalam nama file.
Keuntungannya dibandingkan versi yang hampir setara
getlist() {
find . -iname 'foo*' -print0 | while read -d $'\0' -r file ; do
printf 'File found: %s\n' "$file"
done
}
Apakah itu setiap tugas variabel dalam tubuh loop sementara dipertahankan. Artinya, jika Anda pipa whileseperti di atas maka tubuh whiledalam subkulit yang mungkin tidak seperti yang Anda inginkan.
Keuntungan dari versi substitusi proses find ... -print0 | xargs -0adalah minimal: xargsVersi ini baik-baik saja jika Anda hanya perlu mencetak satu baris atau melakukan satu operasi pada file, tetapi jika Anda perlu melakukan beberapa langkah, versi loop lebih mudah.
EDIT : Ini skrip pengujian yang bagus sehingga Anda bisa mendapatkan ide tentang perbedaan antara berbagai upaya dalam memecahkan masalah ini
#!/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"