เหตุใด printf จึงพิมพ์อาร์กิวเมนต์มากกว่าที่คาดไว้


9

เหตุใดเชลล์สคริปต์การพิมพ์นี้จึงมีอินพุตสองครั้ง

ฉันคาดว่าสคริปต์จะไม่สนใจอินพุตหลังจาก 5

สคริปต์:

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

เอาท์พุท:

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:~$ 

และสคริปต์ต่อไปนี้ทำงานไม่ว่าจะมีการตั้งค่าเป็น $ IFS ทำไม?

#! /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"

เอาท์พุท:

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:~$ 

หยุดprintfได้ตลอดเวลาด้วยการ\cยกเว้นที่เกี่ยวข้องกับตัว%bระบุรูปแบบ printf %s%\ d%b thing 3 "${var+\cquit printing if set}\nelse do a newline" and 0 keep\ going.
ไลค์

คำตอบ:


18

คุณมีสามปัญหา:

  1. ด้วยreadหากมีชื่อตัวแปรน้อยกว่าฟิลด์ในอินพุต var สุดท้ายจะถูกผูกไว้กับฟิลด์ที่เหลือทั้งหมดในบรรทัดด้วยตัวคั่น นั่นหมายความว่า$eจะ5 6เป็นตัวอย่างแรกที่คุณคาดไม่ถึง
  2. เนื่องจาก$a.. ทั้งหมดไม่ได้$eถูกอ้างอิงค่าของมันจึงถูกแบ่งออกเป็นส่วนๆ หาก$eเก็บ " 5 6" มันจะขยายออกเป็นสองอาร์กิวเมนต์ของคำสั่ง
  3. printfกินอาร์กิวเมนต์ทั้งหมดโดยใช้ข้อโต้แย้งจำนวนมากในคราวเดียวเนื่องจากมีการ%แทนที่ซ้ำหลายครั้ง เอกสารนี้ถูกฝังอยู่ในเอกสารดังนี้:

    formatถูกดำเนินการจะต้องนำกลับมาใช้บ่อยเท่าที่จำเป็นเพื่อตอบสนองความถูกดำเนินการโต้แย้ง ตัวระบุพิเศษcหรือsการแปลงใด ๆจะได้รับการประเมินราวกับว่ามีการระบุอาร์กิวเมนต์สตริงแบบ null ข้อกำหนดการแปลงพิเศษอื่น ๆ จะได้รับการประเมินราวกับว่ามีการแจกแจงเป็นศูนย์

    กล่าวอีกนัยหนึ่งหากมีข้อโต้แย้งที่ไม่ได้ใช้มันจะเริ่มต้นใหม่อีกครั้งและประมวลผลพวกเขาตั้งแต่ต้นเกินไปรวมทั้งสตริงรูปแบบทั้งหมด สิ่งนี้มีประโยชน์เมื่อคุณต้องการจัดรูปแบบทั้งอาร์เรย์ให้พูดว่า:

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

    คุณprintfคำสั่งที่ได้รับอาร์กิวเมนต์หนึ่งจากแต่ละ$a.. แล้วอย่างไรก็ดีหลายคนที่เหลือมาจาก$d $eเมื่อ$e" 5 6" printfมีสองรอบไปรอบที่สองเพิ่งจัด6รูปแบบ เมื่อมัน5 6 7 8 9 10มีการทดแทนเต็มรูปแบบสำหรับการพิมพ์ที่สอง


คุณสามารถหลีกเลี่ยงสิ่งเหล่านี้ได้โดยการเพิ่มเขตข้อมูลจำลองพิเศษreadและอ้างถึงการแทนที่พารามิเตอร์ของคุณ (ซึ่งเป็นความคิดที่ดีเสมอ):

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

สิ่งนี้จะให้:

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

dummyรับฟิลด์พิเศษทั้งหมดและprintfรับเพียงห้าอาร์กิวเมนต์ที่คุณคาดไว้


คำถามที่แก้ไขครั้งที่สองของคุณมีคำตอบที่คล้ายกัน: aรับค่าเมื่อIFSไม่มีที่ว่างเท่านั้น นั่นหมายความว่า$b.. $eขยายไปสู่สิ่งใดเลยprintfเพียงแค่โต้แย้งเพียงครั้งเดียว ช่องว่างของคุณจากสตริงรูปแบบจะถูกพิมพ์โดยไม่มีสิ่งใดมาแทนที่ระหว่างพวกเขา ("ราวกับว่ามีการระบุอาร์กิวเมนต์สตริง null")


ฉันทดสอบสคริปต์ที่ 2 อีกครั้งโดยใช้ "$ a" ..... "$ e" สคริปต์ที่สองให้ปัญหาเดิมอีกครั้ง

3
การอ้างอิงจะไม่สร้างความแตกต่างให้กับสคริปต์ที่สอง aมีค่า1 2 3 4 5เป็นสตริงเดี่ยวและได้รับการแทนที่ทั้งหมดในครั้งเดียว
Michael Homer

6
 printf "> %s < " 1 2 3

จะพิมพ์

 > 1 <> 2 <> 3 <

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

พิมพ์

 > 1 2 <> 3  <

printf กินอาร์กิวเมนต์ทั้งหมดเพื่อให้เป็นไปตามสตริงรูปแบบจากนั้นทำซ้ำจนกว่าอาร์กิวเมนต์ทั้งหมดจะถูกประมวลผล

สคริปต์ที่สองใช้งานได้เนื่องจาก$aถูกกำหนดให้เท่านั้นและดังนั้นคำสั่งจะไม่ล้นในการวนซ้ำเพิ่มเติม (มีการวนซ้ำเพียงครั้งเดียวเท่านั้น)


พฤติกรรมนี้ได้รับการบันทึกไว้ในข้อความที่ให้ไว้กับhelp printf:

... รูปแบบจะถูกใช้ซ้ำตามความจำเป็นเพื่อใช้อาร์กิวเมนต์ทั้งหมด หากมีข้อโต้แย้งน้อยกว่ารูปแบบที่ต้องการข้อมูลจำเพาะของรูปแบบเพิ่มเติมจะทำงานเสมือนว่าได้รับค่าศูนย์หรือสตริงที่ว่างตามความเหมาะสม ...

และได้รับคำสั่งจากhttp://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html


ทำไมพฤติกรรมนี้ มันเป็นเอกสารหรือไม่
Shiplu Mokaddim

1
@Shiplu เพิ่มย่อหน้าเกี่ยวกับสถานที่ของเอกสารและมาตรฐานที่ต้องมีพฤติกรรม
PSkocik
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.