แบ่งไฟล์ออกเป็นสองส่วนในรูปแบบ


14

จะแบ่งไฟล์ขนาดใหญ่ออกเป็นสองส่วนได้อย่างไรในรูปแบบ

รับตัวอย่างfile.txt:

ABC
EFG
XYZ
HIJ
KNL

ฉันต้องการที่จะแยกไฟล์นี้ที่XYZดังกล่าวว่าfile1มีเส้นขึ้นไปและส่วนที่เหลือของสายในXYZfile2


ควรXYZรวมสายในเอาท์พุทหรือไม่?
terdon

@terdon ในกรณีของฉันไม่มีบรรทัด "XYZ" ไม่ควรเป็นส่วนหนึ่งของ file2 แต่ถ้าคุณมีวิธีทำโปรดเพิ่มเพื่อตอบ มันอาจมีประโยชน์ในบางกรณี
d.putto

ยุติธรรมพอเสร็จแล้ว
terdon

คำตอบ:


10

ด้วยawkคุณสามารถทำได้:

awk '{print >out}; /XYZ/{out="file2"}' out=file1 largefile


คำอธิบาย:awkอาร์กิวเมนต์แรก( out=file1) กำหนดตัวแปรที่มีชื่อไฟล์ที่จะใช้สำหรับการส่งออกในขณะที่การlargefileประมวลผลอาร์กิวเมนต์ที่ตามมา ( ) awkโปรแกรมจะพิมพ์ทุกสายไปยังแฟ้มที่ระบุโดยตัวแปรout( {print >out}) หากพบรูปแบบXYZตัวแปรเอาต์พุตจะถูกกำหนดใหม่ให้ชี้ไปที่ไฟล์ใหม่ ( {out="file2}") ซึ่งจะใช้เป็นเป้าหมายในการพิมพ์บรรทัดข้อมูลที่ตามมา

อ้างอิง:


14

นี่คืองานสำหรับcsplit:

csplit -sf file -n 1 large_file /XYZ/

จะsilently แยกไฟล์, การสร้างชิ้นก่อนfix fileและnumbered ใช้หลักเดียวเช่นfile0ฯลฯ หมายเหตุว่าการใช้/regex/จะแยกขึ้นไป regexแต่ไม่รวมถึงเส้นที่ตรงกับ หากต้องการแยกและรวมถึงการจับคู่บรรทัดให้regexเพิ่ม+1ออฟเซ็ต:

csplit -sf file -n 1 large_file /XYZ/+1

นี้จะสร้างสองไฟล์ และfile0 file1หากคุณต้องการตั้งชื่ออย่างแน่นอนfile1และfile2คุณสามารถเพิ่มรูปแบบที่ว่างเปล่าลงในcsplitคำสั่งและลบไฟล์แรก:

csplit -sf file -n 1 large_file // /XYZ/+1

สร้างfile0, file1และfile2แต่file0เป็นที่ว่างเปล่าเพื่อให้คุณได้อย่างปลอดภัยสามารถลบออกได้:

rm -f file0

ฉันคิดว่านี่เป็นคำตอบที่ง่ายที่สุด สิ่งที่คุณต้องทำคือแสดงรายการรูปแบบและไฟล์จะถูกแยกตามลำดับ ยอดเยี่ยม!
Henry Blyth

6

ด้วยความทันสมัยkshนี่คือตัวแปรเชลล์ (เช่นไม่มีsed) ของหนึ่งในsedคำตอบพื้นฐานด้านบน:

{ read in <##XYZ ; print "$in" ; cat >file2 ;} <largefile >file1


และอีกตัวแปรหนึ่งอยู่kshคนเดียว (เช่นยังละเว้นcat):

{ read in <##XYZ ; print "$in" ; { read <##"" ;} >file2 ;} <largefile >file1


( kshดูเหมือนว่าวิธีการแก้ปัญหาที่แท้จริงจะค่อนข้าง performant; ในไฟล์ทดสอบ 2.4 GB มันต้องการ 19-21 วินาทีเมื่อเทียบกับ 39-47 วินาทีด้วยวิธีsed/ catตาม)


มันเร็วมาก แต่ฉันไม่คิดว่าคุณต้องการreadและprintคุณควรปล่อยให้มันออกมาเอง ประสิทธิภาพจะดีขึ้นถ้าคุณสร้างชุดเครื่องมือ AST ทั้งหมดและรับkshบิลด์อินทั้งหมดที่รวบรวมมา - มันแปลกสำหรับฉันที่sedไม่ใช่หนึ่งในนั้น แต่ด้วยสิ่งต่าง ๆ เช่นwhile <file doฉันเดาว่าคุณไม่ต้องการsedอะไรมากมาย ...
mikeserv

ฉันสงสัยว่า - การawkแสดงในเกณฑ์มาตรฐานของคุณเป็นอย่างไรบ้าง? และในขณะที่ฉันค่อนข้างแน่ใจว่าkshจะชนะการต่อสู้นี้เสมอหากคุณใช้ GNU sedคุณไม่ยุติธรรมมากsed- ปัญหาของ GNU -uนั้นเป็นวิธีที่ไม่ดีพอที่จะ POSIXLY ทำให้มั่นใจได้ว่าออฟเซ็ตของโปรแกรมจะออกจากโปรแกรม มัน - ไม่จำเป็นต้องชะลอการทำงานปกติของโปรแกรม - การบัฟเฟอร์นั้นดี - สิ่งที่sedควรทำคือดู descriptor เมื่อเสร็จแล้ว ไม่ว่าด้วยเหตุผลใด GNU จะยกเลิกความคิดนั้น
mikeserv

@mikeserv; การจับคู่รูปแบบการเปลี่ยนเส้นทางจะทำจนกว่าจะพบรูปแบบและบรรทัดที่มีรูปแบบที่พบจะไม่ถูกพิมพ์หากไม่ได้ทำอย่างชัดเจนตามที่อธิบายไว้ (อย่างน้อยก็แสดงให้เห็นว่าการทดสอบของฉัน.) หมายเหตุว่าไม่มีwhile; การพิมพ์จะกระทำโดยนัยตามผลข้างเคียงที่กำหนดไว้ของ<##ผู้ดำเนินการเปลี่ยนเส้นทาง และมีเพียงสายการจับคู่ที่จำเป็นต้องพิมพ์ (ด้วยวิธีนี้การใช้งานคุณสมบัติของเชลล์นั้นยืดหยุ่นที่สุดสำหรับการสนับสนุนรวม / excl.) การwhileวนลูปที่ชัดเจนที่ฉันคาดว่าจะช้าลงอย่างมาก (แต่ยังไม่ได้ตรวจสอบ)
Janis

1
@mikeserv; อ่าโอเค. BTW ฉันเพียงแค่พยายามheadแทนread; ดูเหมือนว่าเพียงเล็กน้อยช้า แต่มันรหัส { head -1 <##XYZ ; { read <##"" ;} >file4 ;} <largefile >file3terser:
Janis

1
@mikeserv; จุดดี; มันไม่ใช่ แต่เมื่อฉันเปิดใช้งาน builtin (เพิ่งเสร็จแล้วและตรวจสอบผลลัพธ์) มันเป็นตัวเลขที่แปลกประหลาด (อาจมีบางฟังก์ชั่นการโทรเหนือศีรษะเมื่อเทียบกับการอ่าน?)
Janis

6
{ sed '/XYZ/q' >file1; cat >file2; } <infile

ด้วย GNU sedคุณควรใช้-uสวิตช์ nbuffered ส่วนใหญ่อื่น ๆsedก็ควรทำงานแม้ว่า

หากต้องการออกจาก XYZ ...

{ sed -n '/XYZ/q;p'; cat >file2; } <infile >file1


1

การแฮ็กที่ง่ายคือการพิมพ์ไปที่ STDOUT หรือ STDERR ขึ้นอยู่กับว่ารูปแบบเป้าหมายได้รับการจับคู่หรือไม่ จากนั้นคุณสามารถใช้โอเปอเรเตอร์การเปลี่ยนเส้นทางของเชลล์เพื่อเปลี่ยนทิศทางเอาต์พุตได้ ตัวอย่างเช่นใน Perl สมมติว่าไฟล์อินพุตถูกเรียกใช้fและไฟล์เอาต์พุตสองไฟล์f1และf2:

  1. ทิ้งบรรทัดที่ตรงกับรูปแบบการแยก:

    perl -ne 'if(/XYZ/){$a=1; next} ; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2
  2. รวมถึงการจับคู่สาย:

    perl -ne '$a=1 if /XYZ/; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2

หรือมิฉะนั้นให้พิมพ์ไปที่ตัวจัดการไฟล์ต่าง ๆ :

  1. ทิ้งบรรทัดที่ตรงกับรูปแบบการแยก:

    perl -ne 'BEGIN{open($fh1,">","f1");open($fh2,">","f2");}
    if(/XYZ/){$a=1; next}$a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
    
  2. รวมถึงการจับคู่สาย:

    perl -ne 'BEGIN{open($fh1,">","f1"); open($fh2,">","f2");}
              $a=1 if /XYZ/; $a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
    
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.