Ini bukan hanya gema vs printf
Pertama, mari kita pahami apa yang terjadi dengan read a b c
bagian itu. read
akan melakukan pemisahan kata berdasarkan nilai default IFS
variabel yaitu spasi-tab-baris baru, dan sesuai dengan segalanya berdasarkan itu. Jika ada lebih banyak input daripada variabel untuk menahannya, itu akan cocok bagian yang dibagi menjadi variabel pertama, dan apa yang tidak bisa dipasang - akan masuk ke yang terakhir. Inilah yang saya maksud:
bash-4.3$ read a b c <<< "one two three four"
bash-4.3$ echo $a
one
bash-4.3$ echo $b
two
bash-4.3$ echo $c
three four
Ini persis seperti yang dijelaskan dalam bash
manual (lihat kutipan di akhir jawaban).
Dalam kasus Anda yang terjadi adalah bahwa, 1 dan 2 masuk ke dalam variabel a dan b, dan c mengambil yang lainnya, yaitu 3 4 5 6
.
Apa yang Anda juga akan sering lihat adalah yang digunakan orang while IFS= read -r line; do ... ; done < input.txt
untuk membaca file teks baris demi baris. Sekali lagi, IFS=
ada di sini karena alasan untuk mengontrol pemisahan kata, atau lebih khusus - menonaktifkannya, dan membaca satu baris teks menjadi variabel. Jika tidak ada di sana, read
akan mencoba mencocokkan setiap kata menjadi line
variabel. Tapi itu cerita lain, yang saya anjurkan Anda untuk pelajari nanti, karena while IFS= read -r variable
merupakan struktur yang sangat sering digunakan.
perilaku echo vs printf
echo
melakukan apa yang Anda harapkan di sini. Ini menampilkan variabel Anda persis seperti yang read
telah mereka atur. Ini sudah ditunjukkan dalam diskusi sebelumnya.
printf
sangat istimewa, karena akan terus memasukkan variabel ke dalam format string sampai semuanya habis. Jadi ketika Anda melakukan printf "%d, %d, %d \n" $a $b $c
printf melihat format string dengan 3 desimal, tetapi ada lebih banyak argumen dari 3 (karena variabel Anda benar-benar berkembang menjadi 1,2,3,4,5,6 individu). Ini mungkin terdengar membingungkan, tetapi ada karena alasan peningkatan perilaku dari apa fungsi sebenarnya printf()
tidak dalam bahasa C.
Apa yang Anda lakukan di sini yang mempengaruhi output adalah bahwa variabel Anda tidak dikutip, yang memungkinkan shell (tidak printf
) memecah variabel menjadi 6 item terpisah. Bandingkan ini dengan mengutip:
bash-4.3$ read a b c <<< "1 2 3 4"
bash-4.3$ printf "%d %d %d\n" "$a" "$b" "$c"
bash: printf: 3 4: invalid number
1 2 3
Persis karena $c
variabel dikutip, sekarang diakui sebagai satu string keseluruhan 3 4
,, dan tidak cocok dengan %d
format, yang hanya merupakan integer tunggal
Sekarang lakukan hal yang sama tanpa mengutip:
bash-4.3$ printf "%d %d %d\n" $a $b $c
1 2 3
4 0 0
printf
sekali lagi mengatakan: "OK, Anda memiliki 6 item di sana tetapi format hanya menunjukkan 3, jadi saya akan menyimpan barang dan membiarkan apa pun yang saya tidak cocok dengan input sebenarnya dari pengguna".
Dan dalam semua kasus ini Anda tidak harus mengambil kata saya untuk itu. Jalankan strace -e trace=execve
dan lihat sendiri apa yang sebenarnya perintah "lihat":
bash-4.3$ strace -e trace=execve printf "%d %d %d\n" $a $b $c
execve("/usr/bin/printf", ["printf", "%d %d %d\\n", "1", "2", "3", "4"], [/* 80 vars */]) = 0
1 2 3
4 0 0
+++ exited with 0 +++
bash-4.3$ strace -e trace=execve printf "%d %d %d\n" "$a" "$b" "$c"
execve("/usr/bin/printf", ["printf", "%d %d %d\\n", "1", "2", "3 4"], [/* 80 vars */]) = 0
1 2 printf: ‘3 4’: value not completely converted
3
+++ exited with 1 +++
Catatan tambahan
Seperti yang ditunjukkan Charles Duffy dengan benar di komentar, bash
memiliki built-in sendiri printf
, yang Anda gunakan dalam perintah Anda, strace
sebenarnya akan memanggil /usr/bin/printf
versi, bukan versi shell. Selain perbedaan kecil, untuk minat kami dalam pertanyaan khusus ini penentu format standar adalah sama dan perilaku adalah sama.
Yang juga harus diingat adalah bahwa printf
sintaks jauh lebih portabel (dan karena itu lebih disukai) daripada echo
, belum lagi bahwa sintaksis lebih akrab dengan C atau bahasa seperti C yang memiliki printf()
fungsi di dalamnya. Lihat ini jawaban yang sangat baik dengan Terdon pada subjek printf
vs echo
. Meskipun Anda dapat membuat output yang disesuaikan dengan shell spesifik Anda pada versi spesifik Ubuntu Anda, jika Anda akan mem-porting skrip di berbagai sistem, Anda mungkin harus memilih printf
daripada gema. Mungkin Anda seorang administrator sistem pemula yang bekerja dengan mesin Ubuntu dan CentOS, atau mungkin bahkan FreeBSD - siapa tahu - jadi dalam kasus seperti itu Anda harus membuat pilihan.
Kutipan dari bash manual, bagian SHELL BUILTIN COMMANDS
baca [-ers] [-a aname] [-d delim] [-i teks] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [nama ... ]
Satu baris dibaca dari input standar, atau dari deskriptor file yang disediakan sebagai argumen ke opsi -u, dan kata pertama ditugaskan ke nama depan, kata kedua ke nama kedua, dan seterusnya, dengan sisa kata-kata dan pemisah intervensi mereka ditugaskan untuk nama belakang. Jika ada lebih sedikit kata yang dibaca dari aliran input dari nama, nama yang tersisa diberikan nilai kosong. Karakter dalam IFS digunakan untuk membagi baris menjadi kata-kata menggunakan aturan yang sama yang digunakan shell untuk ekspansi (dijelaskan di bawah di bawah Word Splitting).
strace
kasing dan kasing lainnya -strace printf
menggunakan/usr/bin/printf
, sedangkanprintf
langsung di bash menggunakan shell yang dibangun dengan nama yang sama. Mereka tidak akan selalu identik - misalnya, contoh bash memiliki penentu format%q
dan, dalam versi baru,$()T
untuk pemformatan waktu.