ลองดูตัวอย่างด้วยข้อความอินพุตที่สร้างขึ้นอย่างระมัดระวัง:
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ไม่ ...