จะแบ่งไฟล์ขนาดใหญ่ออกเป็นสองส่วนได้อย่างไรในรูปแบบ
รับตัวอย่างfile.txt
:
ABC
EFG
XYZ
HIJ
KNL
ฉันต้องการที่จะแยกไฟล์นี้ที่XYZ
ดังกล่าวว่าfile1
มีเส้นขึ้นไปและส่วนที่เหลือของสายในXYZ
file2
จะแบ่งไฟล์ขนาดใหญ่ออกเป็นสองส่วนได้อย่างไรในรูปแบบ
รับตัวอย่างfile.txt
:
ABC
EFG
XYZ
HIJ
KNL
ฉันต้องการที่จะแยกไฟล์นี้ที่XYZ
ดังกล่าวว่าfile1
มีเส้นขึ้นไปและส่วนที่เหลือของสายในXYZ
file2
คำตอบ:
ด้วยawk
คุณสามารถทำได้:
awk '{print >out}; /XYZ/{out="file2"}' out=file1 largefile
คำอธิบาย:awk
อาร์กิวเมนต์แรก( out=file1
) กำหนดตัวแปรที่มีชื่อไฟล์ที่จะใช้สำหรับการส่งออกในขณะที่การlargefile
ประมวลผลอาร์กิวเมนต์ที่ตามมา ( ) awk
โปรแกรมจะพิมพ์ทุกสายไปยังแฟ้มที่ระบุโดยตัวแปรout
( {print >out}
) หากพบรูปแบบXYZ
ตัวแปรเอาต์พุตจะถูกกำหนดใหม่ให้ชี้ไปที่ไฟล์ใหม่ ( {out="file2}"
) ซึ่งจะใช้เป็นเป้าหมายในการพิมพ์บรรทัดข้อมูลที่ตามมา
อ้างอิง:
นี่คืองานสำหรับcsplit
:
csplit -sf file -n 1 large_file /XYZ/
จะs
ilently แยกไฟล์, การสร้างชิ้นก่อนf
ix file
และn
umbered ใช้หลักเดียวเช่น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
ด้วยความทันสมัย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
ตาม)
awk
แสดงในเกณฑ์มาตรฐานของคุณเป็นอย่างไรบ้าง? และในขณะที่ฉันค่อนข้างแน่ใจว่าksh
จะชนะการต่อสู้นี้เสมอหากคุณใช้ GNU sed
คุณไม่ยุติธรรมมากsed
- ปัญหาของ GNU -u
นั้นเป็นวิธีที่ไม่ดีพอที่จะ POSIXLY ทำให้มั่นใจได้ว่าออฟเซ็ตของโปรแกรมจะออกจากโปรแกรม มัน - ไม่จำเป็นต้องชะลอการทำงานปกติของโปรแกรม - การบัฟเฟอร์นั้นดี - สิ่งที่sed
ควรทำคือดู descriptor เมื่อเสร็จแล้ว ไม่ว่าด้วยเหตุผลใด GNU จะยกเลิกความคิดนั้น
while
; การพิมพ์จะกระทำโดยนัยตามผลข้างเคียงที่กำหนดไว้ของ<##
ผู้ดำเนินการเปลี่ยนเส้นทาง และมีเพียงสายการจับคู่ที่จำเป็นต้องพิมพ์ (ด้วยวิธีนี้การใช้งานคุณสมบัติของเชลล์นั้นยืดหยุ่นที่สุดสำหรับการสนับสนุนรวม / excl.) การwhile
วนลูปที่ชัดเจนที่ฉันคาดว่าจะช้าลงอย่างมาก (แต่ยังไม่ได้ตรวจสอบ)
head
แทนread
; ดูเหมือนว่าเพียงเล็กน้อยช้า แต่มันรหัส { head -1 <##XYZ ; { read <##"" ;} >file4 ;} <largefile >file3
terser:
{ sed '/XYZ/q' >file1; cat >file2; } <infile
ด้วย GNU sed
คุณควรใช้-u
สวิตช์ nbuffered ส่วนใหญ่อื่น ๆsed
ก็ควรทำงานแม้ว่า
หากต้องการออกจาก XYZ ...
{ sed -n '/XYZ/q;p'; cat >file2; } <infile >file1
ลองด้วย GNU sed:
sed -n -e '1,/XYZ/w file1' -e '/XYZ/,${/XYZ/d;w file2' -e '}' large_file
sed -e '1,/XYZ/{w file1' -e 'd}' large_file > file2
การแฮ็กที่ง่ายคือการพิมพ์ไปที่ STDOUT หรือ STDERR ขึ้นอยู่กับว่ารูปแบบเป้าหมายได้รับการจับคู่หรือไม่ จากนั้นคุณสามารถใช้โอเปอเรเตอร์การเปลี่ยนเส้นทางของเชลล์เพื่อเปลี่ยนทิศทางเอาต์พุตได้ ตัวอย่างเช่นใน Perl สมมติว่าไฟล์อินพุตถูกเรียกใช้f
และไฟล์เอาต์พุตสองไฟล์f1
และf2
:
ทิ้งบรรทัดที่ตรงกับรูปแบบการแยก:
perl -ne 'if(/XYZ/){$a=1; next} ; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2
รวมถึงการจับคู่สาย:
perl -ne '$a=1 if /XYZ/; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2
หรือมิฉะนั้นให้พิมพ์ไปที่ตัวจัดการไฟล์ต่าง ๆ :
ทิ้งบรรทัดที่ตรงกับรูปแบบการแยก:
perl -ne 'BEGIN{open($fh1,">","f1");open($fh2,">","f2");}
if(/XYZ/){$a=1; next}$a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
รวมถึงการจับคู่สาย:
perl -ne 'BEGIN{open($fh1,">","f1"); open($fh2,">","f2");}
$a=1 if /XYZ/; $a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
XYZ
รวมสายในเอาท์พุทหรือไม่?