Mari kita lihat contoh, dengan beberapa teks input yang dibuat dengan cermat:
text=' hello world\
foo\bar'
Itu dua baris, yang pertama dimulai dengan spasi dan diakhiri dengan garis miring terbalik. Pertama, mari kita lihat apa yang terjadi tanpa tindakan pencegahan di sekitarnya read(tetapi gunakan printf '%s\n' "$text"untuk mencetak dengan hati-hati $texttanpa risiko ekspansi). (Di bawah, $ adalah prompt shell.)
$ printf '%s\n' "$text" |
while read line; do printf '%s\n' "[$line]"; done
[hello worldfoobar]
readmemakan backslash: backslash-newline menyebabkan baris baru diabaikan, dan backslash-apa pun mengabaikan backslash pertama. Untuk menghindari backslash diperlakukan secara khusus, kami menggunakan read -r.
$ printf '%s\n' "$text" |
while read -r line; do printf '%s\n' "[$line]"; done
[hello world\]
[foo\bar]
Itu lebih baik, kami memiliki dua garis seperti yang diharapkan. Dua baris hampir berisi konten yang diinginkan: ruang ganda di antara hellodan worldtelah dipertahankan, karena berada dalam linevariabel. Di sisi lain, ruang awal dimakan. Itu karena readmembaca banyak kata saat Anda meneruskan variabel, kecuali bahwa variabel terakhir berisi sisa baris - tetapi masih dimulai dengan kata pertama, yaitu spasi awal dibuang.
Jadi, untuk membaca setiap baris secara harfiah, kita perlu memastikan bahwa tidak ada pemisahan kata yang terjadi. Kami melakukan ini dengan mengatur IFSvariabel ke nilai kosong.
$ printf '%s\n' "$text" |
while IFS= read -r line; do printf '%s\n' "[$line]"; done
[ hello world\]
[foo\bar]
Perhatikan bagaimana kami mengatur IFS secara spesifik untuk durasi readbuilt-in . The IFS= read -r lineset variabel lingkungan IFS(untuk nilai kosong) khusus untuk pelaksanaan read. Ini adalah turunan dari sintaks perintah sederhana umum : urutan penugasan variabel (kemungkinan kosong) diikuti oleh nama perintah dan argumennya (juga, Anda bisa melempar pengalihan kapan saja). Karena readbuilt-in, variabel tidak pernah benar-benar berakhir di lingkungan proses eksternal; namun nilai dari $IFSapa yang kami tetapkan di sana selama readeksekusi long . Perhatikan bahwa readitu bukan built-in khusus , sehingga tugas tidak hanya berlangsung selama durasinya.
Karenanya kami berhati-hati untuk tidak mengubah nilai IFSinstruksi lain yang mungkin bergantung padanya. Kode ini akan berfungsi tidak peduli apa kode yang ditetapkan di IFSawal, dan tidak akan menimbulkan masalah jika kode di dalam loop bergantung IFS.
Berbeda dengan potongan kode ini, yang mencari file di jalur yang dipisahkan titik dua. Daftar nama file dibaca dari file, satu nama file per baris.
IFS=":"; set -f
while IFS= read -r name; do
for dir in $PATH; do
## At this point, "$IFS" is still ":"
if [ -e "$dir/$name" ]; then echo "$dir/$name"; fi
done
done <filenames.txt
Jika loop itu while IFS=; read -r name; do …, maka for dir in $PATHtidak akan terpecah $PATHmenjadi komponen yang dipisahkan oleh titik dua. Jika kode itu IFS=; while read …, akan lebih jelas lagi bahwa IFStidak diatur ke :dalam loop body.
Tentu saja, akan mungkin untuk mengembalikan nilai IFSsetelah dieksekusi read. Tetapi itu akan membutuhkan mengetahui nilai sebelumnya, yang merupakan upaya ekstra. IFS= readadalah cara sederhana (dan, mudah, juga cara terpendek).
¹ Dan, jika readdiinterupsi oleh sinyal yang terperangkap, mungkin saat jebakan mengeksekusi - ini tidak ditentukan oleh POSIX dan tergantung pada shell dalam praktiknya.
while IFS=X readtidak terpecah padaX, tetapiwhile IFS=X; readtidak ...