นามธรรม
พิมพ์บรรทัดที่ไม่มีบรรทัดใหม่เพิ่มบรรทัดใหม่เฉพาะเมื่อมีอีกบรรทัดที่จะพิมพ์
$ printf 'one\ntwo\n' |
awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
โซลูชั่นอื่น ๆ
หากเรากำลังทำงานกับไฟล์เราสามารถตัดทอนตัวละครตัวหนึ่งจากมัน (ถ้ามันลงท้ายด้วยการขึ้นบรรทัดใหม่):
removeTrailNewline () {[[$ (tail -c 1 "$ 1")]] || ตัด -s-1 "$ 1"; }
นั่นเป็นวิธีแก้ปัญหาที่รวดเร็วเนื่องจากต้องการอ่านตัวละครเพียงตัวเดียวจากไฟล์และลบออกโดยตรง ( truncate
) โดยไม่ต้องอ่านไฟล์ทั้งหมด
อย่างไรก็ตามในขณะที่ทำงานกับข้อมูลจาก stdin (สตรีม) ข้อมูลจะต้องอ่านทั้งหมดนี้ และเป็น "บริโภค" ทันทีที่มีการอ่าน ไม่มีการย้อนกลับ (เช่นเดียวกับการตัดทอน) ในการค้นหาจุดสิ้นสุดของสตรีมเราจำเป็นต้องอ่านไปยังจุดสิ้นสุดของสตรีม ณ จุดนี้ไม่มีทางที่จะย้ายกลับไปที่อินพุตสตรีมข้อมูลได้ถูก "ใช้ไป" แล้ว ซึ่งหมายความว่าข้อมูลจะต้องเก็บไว้ในรูปแบบของบัฟเฟอร์บางอย่างจนกว่าเราจะจับคู่ส่วนท้ายของกระแสข้อมูลและทำบางสิ่งกับข้อมูลในบัฟเฟอร์
โซลูชั่นที่ชัดเจนที่สุดคือการแปลงสตรีมเป็นไฟล์และประมวลผลไฟล์นั้น แต่คำถามจะถามตัวกรองบางประเภทของสตรีม ไม่เกี่ยวกับการใช้ไฟล์เพิ่มเติม
ตัวแปร
วิธีการแก้ปัญหาความไร้เดียงสาคือการรวบรวมอินพุตทั้งหมดลงในตัวแปร:
FilterOne(){ filecontents=$(cat; echo "x"); # capture the whole input
filecontents=${filecontents%x}; # Remove the "x" added above.
nl=$'\n'; # use a variable for newline.
printf '%s' "${filecontents%"$nl"}"; # Remove newline (if it exists).
}
printf 'one\ntwo' | FilterOne ; echo 1done
printf 'one\ntwo\n' | FilterOne ; echo 2done
printf 'one\ntwo\n\n' | FilterOne ; echo 3done
หน่วยความจำ
เป็นไปได้ที่จะโหลดไฟล์ทั้งหมดในหน่วยความจำด้วย sed ใน sed มันเป็นไปไม่ได้ที่จะหลีกเลี่ยงการขึ้นบรรทัดใหม่ในบรรทัดสุดท้าย GNU sed อาจหลีกเลี่ยงการพิมพ์บรรทัดใหม่ที่ต่อท้าย แต่เฉพาะในกรณีที่ไฟล์ต้นฉบับหายไป ดังนั้นไม่ง่าย sed ไม่สามารถช่วยได้
ยกเว้น GNU awk ด้วย-z
ตัวเลือก:
sed -z 's/\(.*\)\n$/\1/'
ด้วย awk (awk ใด ๆ ), กวาดทั้งสตรีมและprintf
โดยไม่ต้องขึ้นบรรทัดใหม่
awk ' { content = content $0 RS }
END { gsub( "\n$", "", content ); printf( "%s", content ) }
'
การโหลดไฟล์ทั้งหมดลงในหน่วยความจำอาจไม่ใช่ความคิดที่ดี แต่อาจใช้หน่วยความจำมาก
หน่วยความจำสองบรรทัด
ใน awk เราสามารถประมวลผลสองบรรทัดต่อลูปโดยจัดเก็บบรรทัดก่อนหน้าในตัวแปรและพิมพ์บรรทัดปัจจุบัน:
awk 'NR>1{print previous} {previous=$0} END {printf("%s",$0)}'
การประมวลผลโดยตรง
แต่เราสามารถทำได้ดีกว่า
หากเราพิมพ์บรรทัดปัจจุบันโดยไม่ขึ้นบรรทัดใหม่และพิมพ์บรรทัดใหม่เฉพาะเมื่อมีบรรทัดถัดไปเราจะประมวลผลทีละบรรทัดและบรรทัดสุดท้ายจะไม่มีการขึ้นบรรทัดใหม่:
awk 'NR == 1 {printf ("% s", $ 0); next}; {printf ("\ n% s", $ 0)} '
หรือเขียนด้วยวิธีอื่น:
awk 'NR>1{ print "" }; { printf( "%s", $0 ) }'
หรือ:
awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'
ดังนั้น:
$ printf 'one\ntwo\n' | awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
chomp
เป็นchomp
ลบเพียงมากที่สุดคนหนึ่งลากขึ้นบรรทัดใหม่