ในกรณีที่พบบ่อยที่น่าประหลาดใจซึ่งสิ่งที่คุณต้องทำจริงคือการประมวลผลบรรทัดที่ไม่ว่างทั้งหมดภายในตัวแปรในบางแบบ (รวมถึงการนับ) คุณสามารถตั้งค่า IFS ให้เป็นแค่บรรทัดใหม่แล้วใช้กลไกการแยกคำของเชลล์เพื่อแตก บรรทัดที่ไม่ว่างห่างกัน
ตัวอย่างเช่นต่อไปนี้เป็นฟังก์ชั่นเชลล์เล็ก ๆ ที่รวมบรรทัดที่ไม่ว่างภายในอาร์กิวเมนต์ที่ให้มาทั้งหมด:
lines() (
IFS='
'
set -f #disable pathname expansion
set -- $*
echo $#
)
วงเล็บไม่ใช่วงเล็บปีกกาใช้ที่นี่เพื่อจัดรูปแบบคำสั่งผสมสำหรับเนื้อความฟังก์ชัน สิ่งนี้ทำให้ฟังก์ชั่นดำเนินการใน subshell เพื่อที่จะไม่ก่อให้เกิดมลพิษ IFS ของโลกภายนอกและการตั้งค่าการขยายชื่อพา ธ ในทุกการโทร
หากคุณต้องการวนซ้ำบรรทัดที่ไม่ว่างคุณสามารถทำได้เช่นเดียวกัน:
IFS='
'
set -f
for line in $lines
do
printf '[%s]\n' $line
done
การจัดการ IFS ด้วยวิธีนี้เป็นเทคนิคที่มักถูกมองข้ามนอกจากนี้ยังมีประโยชน์สำหรับการทำสิ่งต่าง ๆ เช่นการแยกวิเคราะห์ชื่อพา ธ ที่อาจมีช่องว่างจากอินพุตคอลัมน์แบบคั่นด้วยแท็บ อย่างไรก็ตามคุณจำเป็นต้องทราบว่าการลบอักขระช่องว่างโดยปกติแล้วรวมอยู่ในการตั้งค่าเริ่มต้นของ IFS ของแท็บช่องว่าง - บรรทัดใหม่สามารถสิ้นสุดการปิดใช้งานการแยกคำในสถานที่ที่คุณคาดว่าจะเห็นปกติ
ตัวอย่างเช่นหากคุณกำลังใช้ตัวแปรเพื่อสร้างบรรทัดคำสั่งที่ซับซ้อนสำหรับบางอย่างเช่นffmpeg
คุณอาจต้องการรวม-vf scale=$scale
เฉพาะเมื่อตัวแปรscale
ถูกตั้งค่าเป็นสิ่งที่ไม่ว่างเปล่า โดยปกติคุณสามารถทำได้ด้วย${scale:+-vf scale=$scale}
แต่ถ้า IFS ไม่รวมอักขระช่องว่างตามปกติในขณะที่การขยายพารามิเตอร์เสร็จสิ้นช่องว่างระหว่าง-vf
และscale=
จะไม่ถูกใช้เป็นตัวคั่นคำและffmpeg
จะถูกส่งผ่านทั้งหมด-vf scale=$scale
เป็นอาร์กิวเมนต์เดียว ซึ่งมันจะไม่เข้าใจ
ในการแก้ไขปัญหาที่คุณจะต้องอย่างใดอย่างหนึ่งเพื่อให้แน่ใจว่าไอเอฟเอได้รับการตั้งค่าอื่น ๆ ได้ตามปกติก่อนที่จะทำในการขยายตัวหรือทำสองขยาย:${scale}
${scale:+-vf} ${scale:+scale=$scale}
คำแบ่งที่เชลล์ทำในกระบวนการแยกวิเคราะห์บรรทัดคำสั่งเริ่มต้นซึ่งตรงข้ามกับการแยกในระหว่างขั้นตอนการขยายการประมวลผลบรรทัดคำสั่งเหล่านั้นไม่ได้ขึ้นอยู่กับ IFS
อย่างอื่นที่อาจคุ้มค่าของคุณในขณะที่ถ้าคุณกำลังทำสิ่งนี้จะสร้างตัวแปรเชลล์โกลบอลสองตัวเพื่อเก็บแท็บและขึ้นบรรทัดใหม่:
t=' '
n='
'
ด้วยวิธีนี้คุณสามารถรวม$t
และ$n
ขยายได้ตามที่คุณต้องการแท็บและการขึ้นบรรทัดใหม่แทนที่จะทิ้งขยะทั้งหมดด้วยช่องว่างที่ยกมา หากคุณต้องการหลีกเลี่ยงช่องว่างที่ยกมาทั้งหมดใน POSIX เชลล์ที่ไม่มีกลไกอื่นที่printf
สามารถทำเช่นนั้นได้คุณสามารถช่วยได้แม้ว่าคุณจะต้องเล่นซอเพื่อแก้ปัญหาการลบบรรทัดใหม่ต่อท้ายในการขยายคำสั่ง:
nt=$(printf '\n\t')
n=${nt%?}
t=${nt#?}
บางครั้งการตั้งค่า IFS ราวกับว่ามันเป็นตัวแปรสภาพแวดล้อมต่อคำสั่งทำงานได้ดี ตัวอย่างเช่นต่อไปนี้เป็นลูปที่อ่านชื่อพา ธ ที่ได้รับอนุญาตให้มีช่องว่างและตัวประกอบสเกลจากแต่ละบรรทัดของไฟล์อินพุตที่คั่นด้วยแท็บ:
while IFS=$t read -r path scale
do
ffmpeg -i "$path" ${scale:+-vf scale=$scale} "${path%.*}.out.mkv"
done <recode-queue.txt
ในกรณีนี้read
บิวด์อินเห็น IFS ตั้งค่าเป็นเพียงแท็บดังนั้นมันจะไม่แยกบรรทัดอินพุตที่อ่านบนช่องว่างด้วย แต่IFS=$t set -- $lines
ไม่ได้ผล: เชลล์ขยาย$lines
เมื่อสร้างset
อาร์กิวเมนต์ของบิวด์ก่อนที่จะดำเนินการคำสั่งดังนั้นการตั้งค่าชั่วคราวของ IFS ในลักษณะที่ใช้เฉพาะระหว่างการดำเนินการของบิวด์อินเองนั้นมาสายเกินไป นี่คือสาเหตุที่โค้ดขนาดเล็กที่ฉันได้ให้ไว้เหนือชุด IFS ทั้งหมดในขั้นตอนที่แยกต่างหากและสาเหตุที่พวกเขาต้องจัดการกับปัญหาของการเก็บรักษาไว้
wc -l
มีค่าเทียบเท่ากับต้นฉบับ:<<<$foo
เพิ่มบรรทัดใหม่ให้กับค่าของ$foo
(แม้ว่าจะ$foo
ว่างเปล่า) ฉันอธิบายในคำตอบของฉันว่าทำไมสิ่งนี้อาจไม่ใช่สิ่งที่ต้องการ แต่เป็นสิ่งที่ถูกถาม