ประมวลผลบรรทัดสุดท้ายก่อนโดยใช้ awk


11

ฉันมีไฟล์ข้อมูลที่ฉันต้องการทำให้เป็นมาตรฐานโดยใช้awkตามดาต้าพอยน์ล่าสุด ดังนั้นฉันต้องการเข้าถึงจุดข้อมูลสุดท้ายก่อนเพื่อทำให้ข้อมูลเป็นมาตรฐานและดำเนินการตามปกติ

วิธีต่อไปนี้ใช้tacสองครั้งเพื่อทำงาน แต่อาจจะซับซ้อนกว่าที่จำเป็น

$ cat file
0 5
1 2
2 3
3 4
$ tac file | awk 'NR==1{norm=$2} {print $1, $2/norm}' | tac
0 1.25
1 0.5
2 0.75
3 1

คำถามของฉันมีดังต่อไปนี้: เป็นไปได้หรือไม่ที่จะได้รับผลลัพธ์ข้างต้นโดยใช้ awk เท่านั้น

ฉันคิดว่าคำตอบคือ "ไม่ awk สแกนไฟล์ทีละบรรทัด" แต่ฉันเปิดให้คำแนะนำสำหรับทางเลือก

คำตอบ:


5

คุณสามารถทำมันเป็นโซลูชั่นแบบสองรอบได้ใน awk:

awk 'FNR == NR { n = $2; next } { print $1, $2/n }' infile infile

หากรุ่น awk ของคุณรองรับบล็อก ENDFILE (เช่น GNU awk 4+) คุณสามารถทำได้ดังนี้:

awk 'ENDFILE { n = $2 } FNR != NR { print $1, $2/n }' infile infile

โปรดทราบว่ามันมีประสิทธิภาพมากขึ้นในการseekที่จะสิ้นสุดของแฟ้มแรกเห็นคำตอบของ camh

คำอธิบาย

ตัวอย่างแรกทำงานโดยการจดจำค่าก่อนหน้า$2นั่นคือจะถูกประเมินเฉพาะเมื่อตัวนับบรรทัดท้องถิ่น ( FNR) เท่ากับตัวนับโกลบอลไลน์ ( NR) nextคำสั่งข้ามยังบรรทัดถัดไปในกรณีนี้จะตรวจสอบว่าบล็อกที่ผ่านมาได้รับการประเมินเฉพาะเมื่ออาร์กิวเมนต์ที่สองจะแยกกัน

ตัวอย่างที่สองมีตรรกะที่คล้ายกัน แต่ใช้ประโยชน์จากบล็อก ENDFILE ซึ่งประเมินผลเมื่อถึงจุดสิ้นสุดของไฟล์อินพุต


ตัวอย่างแรกทำงานได้ดีส่วนที่สองไม่$ awk --version GNU Awk 3.1.8ทำงาน คุณสามารถเพิ่มคำอธิบายเล็ก ๆ น้อย ๆ เกี่ยวกับวิธีจัดการกับไฟล์อินพุตสองไฟล์และอะไรได้nextบ้าง
Bernhard

1
@Bernhard: ดูการแก้ไข
Thor

6

หากแหล่งข้อมูลของคุณเป็นไฟล์ที่สามารถอ่านได้หลายครั้ง (เช่นไม่ใช่สตรีม) อันดับแรกคุณควรใช้tail(1)เพื่อรับข้อมูลที่คุณต้องการจากบรรทัดสุดท้ายและส่งต่อไปยัง awk เพื่อประมวลผลไฟล์ตามลำดับ tailจะพยายามหาจุดสิ้นสุดของไฟล์เพื่ออ่านบรรทัดสุดท้ายโดยไม่จำเป็นต้องอ่านข้อมูลทั้งหมดก่อน

awk -v norm=$(tail -n 1 file | cut -d' ' -f2) '{print $1, $2/norm}' file

นี่จะเป็นชัยชนะครั้งใหญ่ในไฟล์ขนาดใหญ่ที่ไฟล์ทั้งหมดจะไม่พอดีกับบัฟเฟอร์แคช (หมายถึงมันจะต้องอ่านจากดิสก์สองครั้งสำหรับแต่ละรอบ) และจะช่วยในระดับที่เล็กลงโดยไม่จำเป็นต้องสแกน อินพุตเพื่อไปยังบรรทัดสุดท้าย ไฟล์ขนาดเล็กอาจไม่แสดงความแตกต่างอย่างมากกับวิธีการส่งผ่านสองทาง


3

คุณสามารถโหลดมันลงในอาร์เรย์และอ่านย้อนหลังได้:

awk '{x[i++]=$0} END{for (j=i-1; j>=0;) print x[j--] }'

คุณสามารถทำได้อย่างมีประสิทธิภาพมากขึ้น แต่สิ่งนี้แสดงให้เห็นว่าทำไมawkเครื่องมือไม่เหมาะกับสิ่งนี้ ใช้งานต่อไปได้tacถ้ามี GNU tac เป็นเครื่องมือที่เร็วที่สุดสำหรับงานนี้


ฉันเห็นด้วยการใช้for-loops ในawkนั้นไม่ใช่วิธีแก้ปัญหา
แบร์นฮาร์ด
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.