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 $text
tanpa risiko ekspansi). (Di bawah, $
adalah prompt shell.)
$ printf '%s\n' "$text" |
while read line; do printf '%s\n' "[$line]"; done
[hello worldfoobar]
read
memakan 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 hello
dan world
telah dipertahankan, karena berada dalam line
variabel. Di sisi lain, ruang awal dimakan. Itu karena read
membaca 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 IFS
variabel 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 read
built-in . The IFS= read -r line
set 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 read
built-in, variabel tidak pernah benar-benar berakhir di lingkungan proses eksternal; namun nilai dari $IFS
apa yang kami tetapkan di sana selama read
eksekusi long . Perhatikan bahwa read
itu bukan built-in khusus , sehingga tugas tidak hanya berlangsung selama durasinya.
Karenanya kami berhati-hati untuk tidak mengubah nilai IFS
instruksi lain yang mungkin bergantung padanya. Kode ini akan berfungsi tidak peduli apa kode yang ditetapkan di IFS
awal, 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 $PATH
tidak akan terpecah $PATH
menjadi komponen yang dipisahkan oleh titik dua. Jika kode itu IFS=; while read …
, akan lebih jelas lagi bahwa IFS
tidak diatur ke :
dalam loop body.
Tentu saja, akan mungkin untuk mengembalikan nilai IFS
setelah dieksekusi read
. Tetapi itu akan membutuhkan mengetahui nilai sebelumnya, yang merupakan upaya ekstra. IFS= read
adalah cara sederhana (dan, mudah, juga cara terpendek).
¹ Dan, jika read
diinterupsi oleh sinyal yang terperangkap, mungkin saat jebakan mengeksekusi - ini tidak ditentukan oleh POSIX dan tergantung pada shell dalam praktiknya.
while IFS=X read
tidak terpecah padaX
, tetapiwhile IFS=X; read
tidak ...