ลองดูตัวอย่างด้วยข้อความอินพุตที่สร้างขึ้นอย่างระมัดระวัง:
text=' hello world\
foo\bar'
นั่นคือสองบรรทัดเริ่มต้นครั้งแรกด้วยช่องว่างและลงท้ายด้วยแบ็กสแลช ก่อนอื่นมาดูสิ่งที่เกิดขึ้นโดยไม่มีข้อควรระวังread
(แต่ใช้printf '%s\n' "$text"
เพื่อพิมพ์อย่างระมัดระวัง$text
โดยไม่เสี่ยงต่อการขยายตัว) (ด้านล่าง$
เป็นพรอมต์เชลล์)
$ printf '%s\n' "$text" |
while read line; do printf '%s\n' "[$line]"; done
[hello worldfoobar]
read
กินเครื่องหมายแบ็กสแลช: แบ็กสแลช - นิวไลน์ทำให้บรรทัดใหม่ถูกละเว้น, และแบ็กสแลช - อะไรก็ตามจะไม่สนใจแบ็กสแลชแรกนั้น read -r
เพื่อหลีกเลี่ยงการทับขวาได้รับการปฏิบัติเป็นพิเศษที่เราใช้
$ printf '%s\n' "$text" |
while read -r line; do printf '%s\n' "[$line]"; done
[hello world\]
[foo\bar]
ดีกว่าเรามีสองบรรทัดตามที่คาดไว้ ทั้งสองบรรทัดเกือบจะมีเนื้อหาที่ต้องการ: เว้นวรรคระหว่างhello
และworld
ถูกเก็บรักษาไว้เพราะอยู่ในline
ตัวแปร ในทางตรงกันข้ามพื้นที่เริ่มต้นถูกกินหมด นั่นเป็นเพราะread
อ่านคำได้มากเท่าที่คุณส่งผ่านตัวแปรยกเว้นว่าตัวแปรสุดท้ายมีส่วนที่เหลือของบรรทัด - แต่มันยังคงขึ้นต้นด้วยคำแรกนั่นคือช่องว่างเริ่มต้นจะถูกทิ้ง
ดังนั้นเพื่อที่จะอ่านแต่ละบรรทัดอย่างแท้จริงเราจำเป็นต้องตรวจสอบให้แน่ใจว่าไม่มีการแยกคำเกิดขึ้น เราทำสิ่งนี้โดยการตั้งค่าIFS
ตัวแปรเป็นค่าว่าง
$ printf '%s\n' "$text" |
while IFS= read -r line; do printf '%s\n' "[$line]"; done
[ hello world\]
[foo\bar]
โปรดทราบว่าเรากำหนดเป็นIFS
พิเศษสำหรับช่วงเวลาของการread
มีอยู่แล้วภายใน IFS= read -r line
ชุดตัวแปรสภาพแวดล้อมIFS
(เพื่อค่าว่าง) read
โดยเฉพาะสำหรับการดำเนินการของ นี่คืออินสแตนซ์ของไวยากรณ์คำสั่งทั่วไปทั่วไป: ลำดับ (อาจว่างเปล่า) ของการกำหนดตัวแปรตามด้วยชื่อคำสั่งและอาร์กิวเมนต์ (เช่นคุณสามารถโยนในการเปลี่ยนเส้นทางได้ทุกจุด) เนื่องจากread
เป็นบิวด์อินตัวแปรจึงไม่สิ้นสุดในสภาพแวดล้อมของกระบวนการภายนอก อย่างไรก็ตามค่าของ$IFS
คือสิ่งที่เรากำหนดไว้ที่นั่นตราบใดที่read
กำลังดำเนินการ¹ โปรดทราบว่าread
ไม่ใช่การสร้างขึ้นเป็นพิเศษดังนั้นการมอบหมายจะคงอยู่ในระยะเวลาที่กำหนดเท่านั้น
ดังนั้นเราจึงไม่ควรเปลี่ยนค่าของIFS
คำแนะนำอื่น ๆ ที่อาจเชื่อถือได้ รหัสนี้จะทำงานไม่ว่าสิ่งที่รหัสรอบได้มีการกำหนดIFS
ที่จะเริ่มต้นและจะไม่ก่อให้เกิดปัญหาใด ๆ IFS
ถ้ารหัสภายในห่วงอาศัย
ตัดกับโค้ดขนาดสั้นนี้ซึ่งค้นหาไฟล์ในพา ธ ที่คั่นด้วยโคลอน รายการชื่อไฟล์จะอ่านจากไฟล์หนึ่งชื่อไฟล์ต่อบรรทัด
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
ถ้าลูปwhile IFS=; read -r name; do …
นั้นfor dir in $PATH
จะไม่แยก$PATH
เป็นส่วนประกอบที่คั่นด้วยโคลอน ถ้ารหัสเป็นIFS=; while read …
มันจะชัดเจนยิ่งขึ้นที่IFS
ไม่ได้ตั้งค่าไว้:
ในเนื้อหาของลูป
แน่นอนว่ามันจะเป็นไปได้ที่จะเรียกคืนค่าของหลังจากรันIFS
read
แต่นั่นจะต้องรู้ค่าก่อนหน้าซึ่งเป็นความพยายามพิเศษ IFS= read
เป็นวิธีที่ง่าย (และสะดวกยังเป็นวิธีที่สั้นที่สุด)
¹ และถ้าread
ถูกขัดจังหวะโดยสัญญาณที่ติดอยู่อาจเป็นไปได้ในขณะที่กับดักกำลังดำเนินการ - นี้ไม่ได้ระบุโดย POSIX และขึ้นอยู่กับเปลือกในทางปฏิบัติ
while IFS=X read
ไม่แยกที่X
แต่while IFS=X; read
ไม่ ...