Bagaimana cara membagi file besar menjadi dua bagian, pada suatu pola?
Diberikan contoh file.txt:
ABC
EFG
XYZ
HIJ
KNL
Saya ingin membagi file ini XYZsedemikian rupa sehingga file1berisi baris up-to XYZdan sisa baris di file2.
Bagaimana cara membagi file besar menjadi dua bagian, pada suatu pola?
Diberikan contoh file.txt:
ABC
EFG
XYZ
HIJ
KNL
Saya ingin membagi file ini XYZsedemikian rupa sehingga file1berisi baris up-to XYZdan sisa baris di file2.
Jawaban:
Dengan awkAnda dapat melakukan:
awk '{print >out}; /XYZ/{out="file2"}' out=file1 largefile
Penjelasan:awk Argumen pertama ( out=file1) mendefinisikan variabel dengan nama file yang akan digunakan untuk output sementara argumen berikutnya ( largefile) diproses. The awkProgram akan mencetak semua baris ke file yang ditentukan oleh variabel out( {print >out}). Jika pola XYZakan ditemukan, variabel output akan didefinisikan ulang untuk menunjuk ke file baru ( {out="file2}") yang akan digunakan sebagai target untuk mencetak baris data berikutnya.
Referensi:
Ini adalah pekerjaan untuk csplit:
csplit -sf file -n 1 large_file /XYZ/
akan smembagi file, membuat potongan dengan pre fix filedan njumlah menggunakan satu digit, misalnya file0dll. Perhatikan bahwa menggunakan /regex/akan dibagi, tetapi tidak termasuk baris yang cocok regex. Untuk memisahkan dan termasuk pencocokan garis, regextambahkan +1offset:
csplit -sf file -n 1 large_file /XYZ/+1
Ini menciptakan dua file, file0dan file1. Jika Anda benar-benar membutuhkannya untuk diberi nama file1dan file2Anda selalu dapat menambahkan pola kosong ke csplitperintah dan menghapus file pertama:
csplit -sf file -n 1 large_file // /XYZ/+1
membuat file0, file1dan file2tetapi file0kosong sehingga Anda dapat menghapusnya dengan aman:
rm -f file0
Dengan yang modern kshinilah varian shell (yaitu tanpa sed) dari salah satu sedjawaban berdasarkan di atas:
{ read in <##XYZ ; print "$in" ; cat >file2 ;} <largefile >file1
Dan varian lain kshsaja (yaitu juga menghilangkan cat):
{ read in <##XYZ ; print "$in" ; { read <##"" ;} >file2 ;} <largefile >file1
( kshSolusi murni tampaknya cukup performan; pada file uji 2,4 GB dibutuhkan 19-21 detik, dibandingkan dengan 39-47 detik dengan pendekatan sed/ catberbasis).
readdan print- Anda hanya harus membiarkannya untuk menghasilkan sendiri. Performa menjadi lebih baik jika Anda membangun AST toolkit sepenuhnya dan kshmenyusun semua builtin - aneh bagi saya yang sedbukan salah satu dari mereka, sebenarnya. Tetapi dengan hal-hal seperti while <file dosaya kira Anda tidak perlu sedbegitu banyak ...
awkperform di benchmark Anda? Dan sementara saya cukup yakin kshkemungkinan akan selalu memenangkan pertarungan ini, jika Anda menggunakan GNU sedAnda tidak bersikap adil terhadap sed- GNU yang -unbuffered adalah pendekatan yang buruk untuk POSIXLY memastikan offset deskriptor dibiarkan di mana program berhenti itu - seharusnya tidak perlu memperlambat operasi reguler program - buffering baik-baik saja - yang sedharus Anda lakukan hanyalah mencari deskriptor ketika selesai. Untuk alasan apa pun GNU membalikkan mentalitas itu.
while; pencetakan secara implisit dilakukan sebagai efek samping yang ditentukan dari <##operator pengalihan. Dan hanya garis yang cocok yang perlu dicetak. (Dengan demikian implementasi fitur shell paling fleksibel untuk dukungan incl./excl.) Suatu whileloop eksplisit yang saya harapkan akan lebih lambat secara signifikan (tetapi belum dicentang).
headbukan read; tampaknya hanya sedikit lebih lambat, tapi itu kode terser: { head -1 <##XYZ ; { read <##"" ;} >file4 ;} <largefile >file3.
Coba ini dengan sed GNU:
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
Peretasan yang mudah adalah dengan mencetak ke STDOUT atau STDERR, tergantung pada apakah pola target telah cocok. Anda kemudian dapat menggunakan operator pengalihan shell untuk mengarahkan ulang output yang sesuai. Misalnya, dalam Perl, dengan asumsi file input dipanggil fdan dua file output f1dan f2:
Membuang garis yang cocok dengan pola perpecahan:
perl -ne 'if(/XYZ/){$a=1; next} ; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2Termasuk garis yang cocok:
perl -ne '$a=1 if /XYZ/; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2Atau, cetak ke berbagai pegangan file:
Membuang garis yang cocok dengan pola perpecahan:
perl -ne 'BEGIN{open($fh1,">","f1");open($fh2,">","f2");}
if(/XYZ/){$a=1; next}$a==1 ? print $fh1 "$_" : print $fh2 "$_";' fTermasuk garis yang cocok:
perl -ne 'BEGIN{open($fh1,">","f1"); open($fh2,">","f2");}
$a=1 if /XYZ/; $a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
XYZjalur dimasukkan dalam output atau tidak?