ใน `ขณะที่ IFS = อ่าน .. 'ทำไม IFS ถึงไม่มีผลกระทบ?


12

ฉันอาจมีบางอย่างผิดปกติ แต่ดูเหมือนว่าฉันเชื่อว่าการตั้งค่า IFS เป็นหนึ่งในคำสั่งในรายการที่ต้องทำ / เสร็จไม่มีผลอย่างแน่นอน
IFS ภายนอก (ภายนอกwhileโครงสร้าง) จะมีชัยในตัวอย่างทั้งหมดที่แสดงในสคริปต์ด้านล่าง ..

เกิดอะไรขึ้นที่นี่? ฉันเข้าใจผิดว่า IFS ทำอะไรในสถานการณ์นี้หรือไม่? ฉันคาดว่าผลลัพธ์การแบ่งอาร์เรย์จะเป็นไปตามที่แสดงในคอลัมน์ "คาดหวัง"


#!/bin/bash
xifs() { echo -n "$(echo -n "$IFS" | xxd -p)"; } # allow for null $IFS 
show() { x=($1) 
         echo -ne "  (${#x[@]})\t |"
         for ((j=0;j<${#x[@]};j++)); do 
           echo -n "${x[j]}|"
         done
         echo -ne "\t"
         xifs "$IFS"; echo
}
data="a  b   c"
echo -e "-----   --  -- \t --------\tactual"
echo -e "outside        \t  IFS    \tinside" 
echo -e "loop           \t Field   \tloop" 
echo -e "IFS     NR  NF \t Split   \tIFS (actual)" 
echo -e "-----   --  -- \t --------\t-----"
IFS=$' \t\n'; xifs "$IFS"; echo "$data" | while         read; do echo -ne '\t 1'; show "$REPLY"; done 
IFS=$' \t\n'; xifs "$IFS"; echo "$data" | while IFS=    read; do echo -ne '\t 2'; show "$REPLY"; done 
IFS=$' \t\n'; xifs "$IFS"; echo "$data" | while IFS=b   read; do echo -ne '\t 3'; show "$REPLY"; done
IFS=" ";      xifs "$IFS"; echo "$data" | while         read; do echo -ne '\t 4'; show "$REPLY"; done 
IFS=" ";      xifs "$IFS"; echo "$data" | while IFS=    read; do echo -ne '\t 5'; show "$REPLY"; done 
IFS=" ";      xifs "$IFS"; echo "$data" | while IFS=b   read; do echo -ne '\t 6'; show "$REPLY"; done
IFS=;         xifs "$IFS"; echo "$data" | while         read; do echo -ne '\t 7'; show "$REPLY"; done 
IFS=;         xifs "$IFS"; echo "$data" | while IFS=" " read; do echo -ne '\t 8'; show "$REPLY"; done 
IFS=;         xifs "$IFS"; echo "$data" | while IFS=b   read; do echo -ne '\t 9'; show "$REPLY"; done
IFS=b;        xifs "$IFS"; echo "$data" | while IFS=    read; do echo -ne '\t10'; show "$REPLY"; done
IFS=b;        xifs "$IFS"; echo "$data" | while IFS=" " read; do echo -ne '\t11'; show "$REPLY"; done
echo -e "-----   --  -- \t --------\t-----"

เอาท์พุท:

-----   --  --   --------       actual   
outside           IFS           inside                assigned   
loop             Field          loop    #              inner
IFS     NR  NF   Split          IFS     #  expected    IFS
-----   --  --   --------       -----   #  ---------  --------
20090a   1  (3)  |a|b|c|        20090a  #                              
20090a   2  (3)  |a|b|c|        20090a  #  |a  b   c|  IFS=
20090a   3  (3)  |a|b|c|        20090a  #  |a  |   c|  IFS=b
20       4  (3)  |a|b|c|        20      #                          
20       5  (3)  |a|b|c|        20      #  |a  b   c   IFS=
20       6  (3)  |a|b|c|        20      #  |a  |   c|  IFS=b
         7  (1)  |a  b   c|             #                          
         8  (1)  |a  b   c|             #  |a|b|c|     IFS=" "
         9  (1)  |a  b   c|             #  |a  |   c|  IFS=b
62      10  (2)  |a  |   c|     62      #  |a  b   c|  IFS=
62      11  (2)  |a  |   c|     62      #  |a|b|c|     IFS=" "
-----   --  --   --------       -----      ---------   -------                        

คำตอบ:


17

(ขออภัยคำอธิบายที่ยาว)

ใช่IFSตัวแปรในwhile IFS=" " read; do …ไม่มีผลกับส่วนที่เหลือของรหัส

ก่อนอื่นให้แม่นยำว่าบรรทัดคำสั่งเชลล์มีตัวแปรสองชนิด:

  • ตัวแปรเชลล์ (ซึ่งมีอยู่ภายในเชลล์และอยู่ในโลคอลกับเชลล์)
  • ตัวแปรสภาพแวดล้อมซึ่งมีอยู่สำหรับทุกกระบวนการ สิ่งเหล่านี้มักถูกสงวนไว้fork()และexec()ดังนั้นกระบวนการเด็กจึงสืบทอด

เมื่อคุณเรียกคำสั่งด้วย:

  A=foo B=bar command

คำสั่งจะถูกดำเนินการในสภาพแวดล้อมภายในที่ตัวแปร (สภาพแวดล้อม) ความAถูกตั้งค่าfooและมีการตั้งค่าB barแต่ด้วยบรรทัดคำสั่งนี้ตัวแปรเปลือกปัจจุบันAและBจะถูกทิ้งไว้ไม่เปลี่ยนแปลง

สิ่งนี้แตกต่างจาก:

A=foo; B=bar; command

ที่นี่ตัวแปรเชลล์AและBถูกกำหนดและคำสั่งจะรันโดยไม่มีตัวแปรสภาวะแวดล้อมAและBกำหนดไว้ ค่าของAและไม่Bสามารถเข้าถึงcommandได้

อย่างไรก็ตามหากตัวแปรเชลล์บางตัวexport-ed ตัวแปรสภาพแวดล้อมที่สอดคล้องกันจะถูกซิงโครไนซ์กับตัวแปรเชลล์ที่เกี่ยวข้อง ตัวอย่าง:

export A
export B
A=foo; B=bar; command

ด้วยรหัสนี้ทั้งเปลือกตัวแปรและเปลือกสภาพแวดล้อมตัวแปรมีการกำหนดให้และfoo barเนื่องจากตัวแปรสภาพแวดล้อมได้รับการสืบทอดโดยกระบวนการย่อยจึงcommandจะสามารถเข้าถึงค่าได้

หากต้องการย้อนกลับไปยังคำถามเดิมของคุณใน:

IFS='a' read

เพียง แต่readได้รับผลกระทบ และในความเป็นจริงในกรณีreadนี้ไม่สนใจค่าของIFSตัวแปร มันใช้IFSเฉพาะเมื่อคุณขอให้สายที่จะแยก (และเก็บไว้ในตัวแปรหลายอย่าง) เช่นใน:

echo "a :  b :    c" | IFS=":" read i j k; \
    printf "i is '%s', j is '%s', k is '%s'" "$i" "$j" "$k"

IFSไม่ได้ใช้โดยreadถ้ามันถูกเรียกว่ามีข้อโต้แย้ง ( แก้ไข:สิ่งนี้ไม่เป็นความจริงอย่างแน่นอน: อักขระช่องว่างเช่นช่องว่างและแท็บที่ปรากฏในIFSจะถูกละเว้นเสมอที่จุดเริ่มต้น / สิ้นสุดของบรรทัดอินพุต)


ช่างเป็นคำอธิบายที่ดีมาก! มันง่ายมาก! ฉันถูกทำให้สับสนโดยไวยากรณ์'no semi-colon'เป็นเวลาหลายเดือน และมันเป็นเพียงกรณีของมันหมายถึงตัวแปรท้องถิ่น! .. rozcietrzewiaczเปิดทางเดินให้ฉัน (ครั้งใหญ่) ในคำถามอื่น ๆ ... และคุณเพียงแค่ใส่น้ำตาลไอซิ่งบนเค้ก ... ฉันเคย ตลอดทั้งคืนในอันนี้และมันก็คุ้มค่าแน่นอนสำหรับคำตอบที่ดีและชัดเจน! .. ขอบคุณ ..
Peter.O

อืม ฉันต้องอ่านความคิดเห็นที่แก้ไขหลายครั้งก่อนที่ฉันจะได้รับ - คุณหมายถึงว่าตัวละครช่องว่างที่อยู่ใน$IFSนั้นถูกลบออกที่จุดเริ่มต้น / สิ้นสุดของบรรทัดอินพุตฉันเข้าใจหรือไม่ (ซึ่งเป็นวิธีการทำงาน.)
zrajm

โปรดดูที่: unix.stackexchange.com/questions/382963/…

ค่าของ IFS นั้นมีความสำคัญแม้ในขณะที่อ่านตัวแปรเดียวเนื่องจากเชลล์ยังคงทำการแยกคำบนอินพุต ตัวอย่างเช่นการพิมพ์อักขระa<tab>bลงในread varจะทำให้ var มีค่าa<space>bแต่ถ้าคุณมีIFS='<newline>' read varค่า var จะเป็นa<tab>bเช่นนั้นแทน
John Hascall

8

ใส่มันง่ายที่คุณจะต้องอ่านให้กับตัวแปรมากกว่าหนึ่งครั้งสำหรับIFS=<something> read ...สร้างจะมีผลมองเห็นได้ในตัวอย่างของคุณ1

คุณพลาดขอบเขตของreadตัวอย่าง นอกจากนี้ยังไม่มีการปรับเปลี่ยนของไอเอฟเอภายในห่วงในกรณีการทดสอบของคุณ อนุญาตให้ฉันชี้ให้เห็นอย่างแน่ชัดว่า IFS ตัวที่สองจะมีผลกระทบในแต่ละบรรทัดของคุณอย่างไร:

 IFS=$' \t\n'; xifs "$IFS"; echo "$data" | while IFS=b   read; do echo ...
                                                      ^      ^
                                                      |      |
                                          from here --'       `- to here :)

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

 $ data="a  b   c"
 $ echo "$data" | while           read A B C; do echo \|$A\|$B\|\|$C\|; done
 |a|b||c|
 $ echo "$data" | while IFS=      read A B C; do echo \|$A\|$B\|\|$C\|; done
 |a b c||||
 $ echo "$data" | while IFS='a'   read A B C; do echo \|$A\|$B\|\|$C\|; done
 || b c|||
 $ echo "$data" | while IFS='ab'  read A B C; do echo \|$A\|$B\|\|$C\|; done
 || || c|

1ตามที่ฉันเพิ่งเรียนรู้จาก Gillesอาจมีประโยชน์ในการตั้งค่าIFS=''(ว่าง) เมื่ออ่านเพียงหนึ่งฟิลด์: มันหลีกเลี่ยงการตัดทอนของช่องว่างที่จุดเริ่มต้นของบรรทัด


ดี .. ขอบคุณ ... ผมได้รับมันในเวลานี้ .. และฉันรักร่างของคุณ :)
Peter.O

ตกลงตอนนี้ฉันได้อ่านความคิดเห็นของคุณแล้วว่าคุณไม่ได้สังเกตเห็นคำตอบของฉันสำหรับปัญหานั้นในคำถามอื่น บางทีคุณสามารถย้อนกลับรายการอื่นและลบรายการนี้เนื่องจากเป็นปัญหาทั่วไปข้อเดียว
rozcietrzewiacz

ใช่คำถามสองข้อมีประเด็นที่เกี่ยวข้อง แต่ชื่อเรื่องของอีกคำถามหนึ่งคือ "เหตุใดจึงIFS= readใช้ในการตั้งค่าเพื่อตั้งค่าตัวแปรสภาพแวดล้อม IFS อีกครั้ง" ฉันไม่ได้รับรู้ดังนั้นตัวแปรท้องถิ่นสามารถตั้งค่าได้โดยผู้เรียกคำสั่ง นั่นคือคำตอบสำหรับคำถามนั้น มันมีวิวัฒนาการต่อไปเมื่อพูดถึงประเด็นหลักของคำถามนี้ แต่เมื่อฉันตระหนักว่าฉันได้ถามคำถามนี้ไปแล้ว ... บางทีคำถามทั้งสองอาจคล้ายกันกับสองsedคำถามชื่อเพิ่มเติมสำหรับ googler to google
Peter.O
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.