ไตรมาสที่ 1 การแยกฟิลด์
การแบ่งฟิลด์เหมือนกับการแยกคำหรือไม่
ใช่ทั้งคู่ชี้ไปที่แนวคิดเดียวกัน
การตั้งค่าIFS=''
เป็นค่าว่างเช่นเดียวกับสตริงว่างเปล่าด้วยหรือไม่
ใช่ทั้งสามหมายถึงเหมือนกัน: จะไม่มีการแยกฟิลด์ / คำ นอกจากนี้ยังส่งผลต่อฟิลด์การพิมพ์ (เช่นเดียวกับecho "$*"
) ฟิลด์ทั้งหมดจะถูกต่อกันพร้อมกันโดยไม่มีที่ว่าง
Q3: (ส่วนหนึ่ง) Unset IFS
ในข้อมูลจำเพาะ POSIX ฉันอ่านต่อไปนี้ :
ถ้าไอเอฟเอไม่ได้ตั้งเปลือกจะประพฤติตัวเป็นถ้าค่าของไอเอฟเอเป็น<พื้นที่> <แท็บ> <newline>
ซึ่งเทียบเท่ากับ:
ด้วยunset IFS
เปลือกจะทำตัวราวกับว่า IFS เป็นค่าเริ่มต้น
นั่นหมายความว่า 'การแยกฟิลด์' จะเหมือนกันทุกประการด้วยค่า IFS เริ่มต้นหรือไม่ได้ตั้งค่า
นั่นไม่ได้หมายความว่า IFS จะทำงานในลักษณะเดียวกันในทุกสภาวะ การเจาะจงมากขึ้นการเรียกใช้งานOldIFS=$IFS
จะตั้งค่า var OldIFS
เป็นnullไม่ใช่ค่าเริ่มต้น และพยายามตั้งค่า IFS กลับเช่นนี้IFS=OldIFS
จะตั้งค่า IFS เป็น null ไม่เก็บไว้เหมือนเมื่อก่อน ระวัง !!.
Q3: (ส่วน b) กู้คืน IFS
ฉันจะคืนค่า IFS เป็นค่าเริ่มต้นได้อย่างไร ว่าฉันต้องการคืนค่าเริ่มต้นของ IFS ฉันจะทำอย่างไร (โดยเฉพาะอย่างยิ่งฉันจะอ้างถึง<tab>และ<newline> ได้อย่างไร)
สำหรับ zsh, ksh และ bash (AFAIK) สามารถตั้งค่า IFS เป็นค่าเริ่มต้นเป็น:
IFS=$' \t\n' # works with zsh, ksh, bash.
เสร็จแล้วคุณต้องอ่านอะไรอีก
แต่ถ้าคุณต้องการตั้งค่า IFS ใหม่สำหรับ sh มันอาจซับซ้อน
ลองมาดูจากง่ายที่สุดเพื่อให้เสร็จสมบูรณ์โดยไม่มีข้อบกพร่อง (ยกเว้นความซับซ้อน)
1.- ยกเลิก IFS
เราทำได้unset IFS
(อ่านไตรมาสที่ 3 ก, เหนือ)
2.- แลกเปลี่ยนตัวอักษร
การแก้ปัญหาการสลับค่าของแท็บและการขึ้นบรรทัดใหม่ทำให้การตั้งค่าของ IFS ง่ายขึ้นจากนั้นจะทำงานในลักษณะที่เท่าเทียมกัน
ตั้งค่า IFS เป็น<space><newline> <tab> :
sh -c 'IFS=$(echo " \n\t"); printf "%s" "$IFS"|xxd' # Works.
3.- ง่าย? วิธีการแก้:
หากมีสคริปต์ลูกที่ต้องตั้งค่า IFS อย่างถูกต้องคุณสามารถเขียนด้วยตนเองได้เสมอ:
ไอเอฟเอ ='
'
โดยที่ลำดับที่พิมพ์ด้วยตนเองคือ: IFS=
'spacetabnewline'ลำดับที่พิมพ์จริงอย่างถูกต้องด้านบน (หากคุณต้องการยืนยันให้แก้ไขคำตอบนี้) แต่การคัดลอก / วางจากเบราว์เซอร์ของคุณจะแตกเพราะเบราว์เซอร์จะบีบ / ซ่อนช่องว่าง มันทำให้ยากที่จะแบ่งปันรหัสตามที่เขียนไว้ข้างต้น
4.- โซลูชั่นที่สมบูรณ์
ในการเขียนโค้ดที่สามารถคัดลอกได้อย่างปลอดภัยมักจะเกี่ยวข้องกับการหลีกเลี่ยงการพิมพ์ที่ชัดเจน
เราต้องการรหัสที่ "สร้าง" ค่าที่คาดหวัง แต่แม้ว่าจะถูกต้องในเชิงแนวคิดรหัสนี้จะไม่ตั้งค่าต่อท้าย\n
:
sh -c 'IFS=$(echo " \t\n"); printf "%s" "$IFS"|xxd' # wrong.
ที่เกิดขึ้นเนื่องจากภายใต้เชลล์ส่วนใหญ่บรรทัดใหม่ต่อท้าย$(...)
หรือการ`...`
แทนที่คำสั่งทั้งหมดจะถูกลบออกเมื่อมีการขยาย
เราจำเป็นต้องใช้เคล็ดลับสำหรับการดวลจุดโทษ:
sh -c 'IFS="$(printf " \t\nx")"; IFS="${IFS%x}"; printf "$IFS"|xxd' # Correct.
ทางเลือกอื่นอาจเป็นการตั้งค่า IFS เป็นค่าสภาพแวดล้อมจาก bash (ตัวอย่าง) และจากนั้นเรียก sh (รุ่นที่ยอมรับ IFS ที่จะตั้งค่าผ่านสภาพแวดล้อม) ดังนี้:
env IFS=$' \t\n' sh -c 'printf "%s" "$IFS"|xxd'
ในระยะสั้น sh ทำให้การรีเซ็ต IFS เป็นค่าเริ่มต้นค่อนข้างเป็นการผจญภัยที่แปลก
Q4: ในรหัสจริง:
สุดท้ายรหัสนี้จะเป็นอย่างไร:
while IFS= read -r line
do
echo $line
done < /path_to_text_file
ประพฤติถ้าเราเปลี่ยนบรรทัดแรกเป็น
while read -r line # Use the default IFS value
หรือถึง:
while IFS=' ' read -r line
ครั้งแรก: ฉันไม่ทราบว่าecho $line
(มี var ไม่ได้อ้างอิง) มีอยู่ใน porpouse หรือไม่ มันแนะนำระดับที่สองของ 'การแยกฟิลด์' ที่อ่านไม่ได้ ดังนั้นฉันจะตอบทั้งคู่ :)
ด้วยรหัสนี้ (เพื่อให้คุณสามารถยืนยันได้) คุณจะต้องมีประโยชน์ xxd :
#!/bin/ksh
# Correctly set IFS as described above.
defIFS="$(printf " \t\nx")"; defIFS="${defIFS%x}";
IFS="$defIFS"
printf "IFS value: "
printf "%s" "$IFS"| xxd -p
a=' bar baz quz '; l="${#a}"
printf "var value : %${l}s-" "$a" ; printf "%s\n" "$a" | xxd -p
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf 'Values quoted :\n' "" # With values quoted:
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS default quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf '%s\n' "Values unquoted :" # Now with values unquoted:
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- unquoted : "
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS defau unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
ฉันเข้าใจ:
$ ./stackexchange-Understanding-IFS.sh
IFS value: 20090a
var value : bar baz quz -20202062617220202062617a20202071757a2020200a
IFS --x-- : bar baz quz -20202062617220202062617a20202071757a202020
Values quoted :
IFS null quoted : bar baz quz -20202062617220202062617a20202071757a202020
IFS default quoted : bar baz quz-62617220202062617a20202071757a
IFS unset quoted : bar baz quz-62617220202062617a20202071757a
IFS space quoted : bar baz quz-62617220202062617a20202071757a
Values unquoted :
IFS --x-- unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS null unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS defau unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS unset unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS space unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
ค่าแรกเป็นเพียงค่าที่ถูกต้องของ IFS=
'spacetabnewline'
บรรทัดถัดไปคือค่า$a
เลขฐานสิบหกทั้งหมดที่ var มีและขึ้นบรรทัดใหม่ '0a' ที่ส่วนท้ายซึ่งจะถูกกำหนดให้กับแต่ละคำสั่ง read
บรรทัดถัดไปซึ่ง IFS เป็นโมฆะจะไม่ทำการ 'แยกฟิลด์' แต่จะขึ้นบรรทัดใหม่ (ตามที่คาดไว้)
สามบรรทัดถัดไปเนื่องจาก IFS มีช่องว่างให้ลบช่องว่างเริ่มต้นและตั้งค่าบรรทัด var เป็นยอดเงินคงเหลือ
สี่บรรทัดสุดท้ายแสดงให้เห็นว่าตัวแปรที่ไม่มีเครื่องหมายจะทำอะไร ค่าจะถูกแบ่งในพื้นที่ (หลาย) และจะถูกพิมพ์เป็น:bar,baz,qux,
IFS
และ unsetIFS
แตกต่างกันมาก คำตอบสำหรับไตรมาสที่ 4 นั้นผิดพลาดบางส่วน: ตัวคั่นภายในไม่ได้ถูกแตะต้องที่นี่เพียงนำหน้าและต่อท้ายเท่านั้น