โหมด Slurp ใน awk?


16

เครื่องมือชอบsed, awkหรือperl -nดำเนินการอย่างใดอย่างหนึ่งใส่ของพวกเขาบันทึกในเวลาที่บันทึกเป็นเส้นโดยค่าเริ่มต้น

บางอย่างเช่นawkกับRSแอฟริกาsedด้วย-zหรือperlกับ-0oooสามารถเปลี่ยนชนิดของการบันทึกโดยการเลือกตัวคั่นบันทึกที่แตกต่างกัน

perl -nสามารถสร้างอินพุตทั้งหมด (แต่ละไฟล์เมื่อผ่านหลายไฟล์) เป็นเรคคอร์ดเดียวพร้อม-0777ตัวเลือก (หรือ-0ตามด้วยหมายเลขฐานแปดที่มากกว่า 0377, 777 เป็นแบบบัญญัติ) ว่าสิ่งที่พวกเขาเรียกว่าโหมด Slurp

สิ่งที่คล้ายกันสามารถทำได้ด้วยawk' RSหรือกลไกอื่น ๆ ? ที่ไหนawkประมวลผลแต่ละไฟล์เนื้อหาโดยรวมในการสั่งซื้อเมื่อเทียบกับแต่ละบรรทัดของแต่ละไฟล์?

คำตอบ:


15

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

gawk, mawkหรืออื่น ๆawkการใช้งานที่RSสามารถกับ regexp

ในการใช้งานเหล่านั้น (สำหรับmawkระวังว่า OS บางรุ่นเช่น Debian จัดส่งเวอร์ชันเก่ามากแทนที่จะเป็น modern ที่ดูแลโดย @ThomasDickey ) หากRSมีอักขระตัวเดียวตัวคั่นเร็กคอร์ดคืออักขระนั้นหรือawkเข้าสู่โหมดย่อหน้าเมื่อRSว่างเปล่า หรือถือว่าRSเป็นการแสดงออกปกติ

วิธีแก้ปัญหาคือใช้นิพจน์ทั่วไปที่ไม่สามารถจับคู่ได้ บางคนคิดเหมือนx^หรือ$x( xก่อนเริ่มหรือหลังจบ) อย่างไรก็ตามบางคน (โดยเฉพาะกับgawk) มีราคาแพงกว่าคนอื่น ๆ จนถึงตอนนี้ฉันพบว่า^$มีประสิทธิภาพมากที่สุด สามารถจับคู่กับอินพุตว่างเท่านั้น แต่จากนั้นจะไม่มีสิ่งใดเทียบได้

ดังนั้นเราสามารถทำ:

awk -v RS='^$' '{printf "%s: <%s>\n", FILENAME, $0}' file1 file2...

หนึ่งข้อแม้คือว่ามันข้ามไฟล์เปล่า (ตรงกันข้ามperl -0777 -n) ที่สามารถแก้ไขได้ด้วย GNU awkโดยใส่รหัสลงในENDFILEคำสั่งแทน แต่เราจำเป็นต้องรีเซ็ต$0ในคำสั่ง BEGINFILE เพราะจะไม่ถูกรีเซ็ตหลังจากประมวลผลไฟล์ว่าง:

gawk -v RS='^$' '
   BEGINFILE{$0 = ""}
   ENDFILE{printf "%s: <%s>\n", FILENAME, $0}' file1 file2...

awkการใช้งานแบบดั้งเดิมPOSIXawk

ในนั้นRSเป็นเพียงหนึ่งตัวอักษรพวกเขาไม่มีBEGINFILE/ ENDFILEพวกเขาไม่มีRTตัวแปรพวกเขายังไม่สามารถประมวลผลอักขระ NUL ได้

คุณอาจคิดว่าการใช้RS='\0'งานสามารถใช้งานได้เนื่องจากไม่สามารถประมวลผลอินพุตที่มี NUL byte ได้ แต่ไม่ใช่ว่าRS='\0'ในการปรับใช้แบบดั้งเดิมจะได้รับการปฏิบัติเหมือนRS=ซึ่งเป็นโหมดย่อหน้า

\1ทางออกหนึ่งที่จะสามารถใช้ตัวอักษรที่ไม่น่าเป็นไปได้ที่จะพบในการป้อนข้อมูลเช่นที่ ในโลแคลอักขระหลายไบต์คุณสามารถทำให้เป็นลำดับไบต์ที่ไม่น่าจะเกิดขึ้นเนื่องจากเป็นอักขระที่ไม่ได้รับมอบหมายหรือไม่ใช่อักขระเช่น$'\U10FFFE'ในโลแคล UTF-8 ไม่เข้าใจผิดจริง ๆ แม้ว่าและคุณมีปัญหากับไฟล์ว่างเปล่าเช่นกัน

อีกวิธีหนึ่งคือการเก็บข้อมูลทั้งหมดในตัวแปรและดำเนินการในคำสั่ง END ที่สิ้นสุด ซึ่งหมายความว่าคุณสามารถประมวลผลได้ครั้งละหนึ่งไฟล์เท่านั้น:

awk '{content = content $0 RS}
     END{$0 = content
       printf "%s: <%s>\n", FILENAME, $0
     }' file

นั่นเท่ากับsed:

sed '
  :1
  $!{
   N;b1
  }
  ...' file1

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


สำหรับส่วนสุดท้าย ("หากไฟล์ไม่ได้ลงท้ายด้วยอักขระขึ้นบรรทัดใหม่ (และไม่ว่าง) จะยังคงเพิ่มอีกหนึ่งไฟล์ใน $ 0 ตอนท้าย"): สำหรับไฟล์ข้อความพวกเขาควรจะมีจุดสิ้นสุด ขึ้นบรรทัดใหม่ ตัวอย่างเช่น vi เพิ่มหนึ่งไฟล์และทำการแก้ไขไฟล์เมื่อคุณบันทึก การไม่มีการขึ้นบรรทัดใหม่ทำให้คำสั่งบางรายการยกเลิก "บรรทัด" สุดท้าย (เช่น: wc) แต่คนอื่น ๆ ยังคงเห็นบรรทัดสุดท้าย ... ymmv วิธีการแก้ปัญหาของคุณจึงถูกต้อง imo ถ้าคุณควรจะรักษาไฟล์ข้อความ (ซึ่งอาจเป็นเพราะ awk นั้นดีสำหรับการประมวลผลข้อความ แต่ไม่ดีสำหรับไบนารี ^^)
Olivier Dulac

1
การพยายามที่จะหลบเลี่ยงทั้งหมดอาจมีข้อ จำกัด บางประการ ... ขนบธรรมเนียมประเพณี awk เห็นได้ชัดว่ามี (มี?) จำกัด 99 เขตข้อมูลในบรรทัด ... ดังนั้นคุณอาจต้องใช้ FS ที่แตกต่างกันเช่นกันเพื่อหลีกเลี่ยงขีด จำกัด นั้น แต่คุณอาจ ยังมีข้อ จำกัด เกี่ยวกับความยาวรวมของบรรทัด (หรือทั้งหมดถ้าคุณจัดการเพื่อให้ได้ทั้งหมดในหนึ่งบรรทัด) สามารถ?
Olivier Dulac

ในที่สุด: แฮ็ค (เซ่อ ... ) อาจจะเป็นอันดับที่ 1 ในการแยกไฟล์ทั้งหมดและมองหา char ที่ไม่ได้อยู่ในนั้นจากนั้น tr '\n' 'thatchar' ไฟล์ก่อนที่จะส่งไปยัง awk และtr 'thatchar' \n'เอาท์พุท? (คุณอาจต้องต่อท้ายบรรทัดใหม่เพื่อให้มั่นใจว่าเช่นเดียวกับที่ฉันบันทึกไว้ข้างต้นไฟล์อินพุตของคุณมีการขึ้นบรรทัดใหม่: { tr '\n' 'missingchar' < thefile ; printf "\n" ;} | awk ..... | { tr 'missingchar' '\n' }(แต่นั่นเพิ่ม '\ n' ในท้ายที่สุดว่าคุณอาจต้องกำจัด ... อาจจะ เพิ่ม sed ก่อน TR สุดท้ายถ้า TR ที่ยอมรับไฟล์โดยไม่ต้องยุติการขึ้นบรรทัดใหม่ ... )
โอลิเวีย Dulac

@OlivierDulac ขีด จำกัด ของจำนวนฟิลด์จะได้รับผลกระทบก็ต่อเมื่อเราเข้าถึง NF หรือสาขาใด ๆ awkไม่แยกถ้าเราไม่ ต้องบอกว่าไม่ใช่แม้แต่/bin/awkของ Solaris 9 (ตามยุค 1970 awk) มีข้อ จำกัด ดังนั้นฉันไม่แน่ใจว่าเราสามารถหาสิ่งที่ทำได้ เป็นไปได้ว่าการเพิ่มขีด จำกัด นั้นถูกเพิ่มโดย Sun และอาจไม่พบใน awks อื่น ๆ ของ SVR4 คุณสามารถทดสอบบน AIX ได้หรือไม่)
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.