Mengapa printf mencetak lebih banyak argumen dari yang diharapkan?

9

Mengapa input pencetakan skrip shell ini dua kali?

Saya mengharapkan skrip untuk mengabaikan input setelah 5.

Naskah:

#! /bin/bash
echo "Enter 5 words : "
read  a b c d e 
printf "> %s %s %s %s %s <" $a $b $c $d $e

Keluaran:

user@linux:~$ pico ifs2.sh
user@linux:~$ ./ifs2.sh
Enter 5 words : 
1 2 3 4 5 
> 1 2 3 4 5 <user@linux:~$ ./ifs2.sh
Enter 5 words : 
1 2 3 4 5 6
> 1 2 3 4 5 <> 6     <user@linux:~$ ./ifs2.sh
Enter 5 words : 
1 2 3 4 5 6 7 8 9 0
> 1 2 3 4 5 <> 6 7 8 9 0 <user@linux:~$ 

Dan, skrip berikut berfungsi apa pun yang disetel ke $ IFS. Mengapa?

#! /bin/bash    
old="$IFS"
IFS=":"
echo "IFS = $IFS"
echo "Enter 5 words : "
read  a b c d e 
printf "> %s %s %s %s %s <" $a $b $c $d $e    
IFS="$old"

Keluaran:

user@linux:~$ ./ifs2.sh
IFS = :
Enter 5 words : 
1 2 3 4 5  
> 1 2 3 4 5      <user@linux:~$ ./ifs2.sh
IFS = :
Enter 5 words : 
1 2 3 4 5
> 1 2 3 4 5     <user@linux:~$ ./ifs2.sh
IFS = :
Enter 5 words : 
1:2:3:4:5
> 1 2 3 4 5 <user@linux:~$ 
mikeserv
sumber
berhenti printfkapan saja dengan \cpelarian yang terkait dengan %bpenentu format. Seperti:printf %s%\ d%b thing 3 "${var+\cquit printing if set}\nelse do a newline" and 0 keep\ going.
mikeserv

Jawaban:

18

Anda memiliki tiga masalah:

  1. Dengan read, jika ada lebih sedikit nama variabel daripada bidang dalam input, var terakhir akan terikat ke semua bidang yang tersisa di baris, dengan pembatas. Itu berarti bahwa Anda $emendapatkan 5 6contoh pertama yang tidak terduga.
  2. Karena semua $a.. $etidak dikutip, nilainya mengalami pemisahan bidang . Jika $ememegang " 5 6" maka itu berkembang menjadi dua argumen pada perintah.
  3. printfmenghabiskan semua argumennya, menggunakan banyak argumen sekaligus karena ada %pergantian, berulang kali. Ini terkubur dalam dokumentasi sebagai:

    The formatoperan akan digunakan kembali sesering yang diperlukan untuk memenuhi operan argumen. Setiap penentu tambahan catau skonversi harus dievaluasi seolah-olah argumen string nol diberikan; spesifikasi konversi tambahan lainnya harus dievaluasi seolah-olah tidak ada argumen yang diberikan.

    Dengan kata lain, jika ada argumen yang tidak digunakan itu mulai lagi dan memprosesnya dari awal juga, termasuk seluruh string format. Ini berguna ketika Anda ingin memformat seluruh array, katakan:

    printf '%b ' "${array[@]}"

    printfPerintah Anda mendapat satu argumen dari masing-masing $a.. $d, dan kemudian banyak yang tersisa dari $e. Kapan $e" 5 6", printfada dua yang berputar, yang kedua baru saja 6memformat. Saat itu 5 6 7 8 9 10ia memiliki berbagai macam pengganti untuk pencetakan kedua.


Anda dapat menghindari semua ini dengan menambahkan bidang boneka tambahan read, dan mengutip penggantian parameter Anda (yang selalu merupakan ide bagus):

read  a b c d e dummy
printf "> %s %s %s %s %s <" "$a" "$b" "$c" "$d" "$e"

Ini akan memberi:

Enter 5 words : 
1 2 3 4 5 6 7 8 9 10
> 1 2 3 4 5 <

dummymendapatkan semua bidang tambahan, dan printfhanya mendapatkan lima argumen yang Anda harapkan.


Pertanyaan Anda yang diedit kedua memiliki jawaban yang sama: hanya amendapat nilai bila IFStidak memiliki spasi. Itu berarti $b.. $eberkembang menjadi nol, jadi printfhanya mendapat satu argumen. Spasi Anda dari string format dicetak, dengan tidak ada yang diganti di antaranya ("seolah-olah argumen string nol disediakan").

Michael Homer
sumber
Saya kembali menguji skrip ke-2 menggunakan "$ a" ..... "$ e". Script kedua memberikan masalah yang sama lagi.
3
Mengutip tidak akan membuat perbedaan pada skrip kedua. amemiliki nilai 1 2 3 4 5sebagai string tunggal dan akan diganti sekaligus.
Michael Homer
6
 printf "> %s < " 1 2 3

akan dicetak

 > 1 <> 2 <> 3 <

  printf "> %s %s <" 1 2 3

cetakan

 > 1 2 <> 3  <

printf makan semua argumen untuk memenuhi format string dan kemudian ulangi sampai semua argumen diproses.

Script kedua berfungsi karena hanya $aditugaskan dan oleh karena itu perintah tidak meluap ke iterasi tambahan (hanya ada satu iterasi).


Perilaku ini didokumentasikan dalam teks yang dilengkapi dengan help printf:

... Format digunakan kembali seperlunya untuk menggunakan semua argumen. Jika ada lebih sedikit argumen daripada yang dibutuhkan format, spesifikasi format ekstra berperilaku seolah-olah nilai nol atau string nol, sebagaimana mestinya, telah disediakan. ...

dan diamanatkan oleh http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html

PSkocik
sumber
Mengapa perilaku ini? apakah ini didokumentasikan?
Shiplu Mokaddim
1
@Shiplu menambahkan paragraf tentang di mana perilaku didokumentasikan dan standar yang mengharuskan perilaku tersebut.
PSkocik