pisahkan file menjadi dua bagian, sesuai pola

14

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.

d.putto
sumber
Haruskah XYZjalur dimasukkan dalam output atau tidak?
terdon
@terdon Dalam kasus saya, tidak ada baris "XYZ" tidak boleh menjadi bagian dari file2. Tetapi jika Anda memiliki cara untuk melakukan itu, harap tambahkan ke jawaban. Mungkin bermanfaat dalam beberapa kasus lain.
d.putto
Cukup adil, sudah selesai.
terdon

Jawaban:

10

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:

Janis
sumber
14

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
don_crissti
sumber
Saya kira ini adalah jawaban yang paling sederhana. Yang harus Anda lakukan adalah mendaftar beberapa pola dan file tersebut akan dibagi berdasarkan urutannya. Cemerlang!
Henry Blyth
6

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).

Janis
sumber
Ini sangat cepat. Tapi saya pikir Anda tidak perlu 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 ...
mikeserv
Saya ingin tahu - bagaimana 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.
mikeserv
@ mikeserv; Pencocokan pola pengalihan dilakukan sampai pola ditemukan, dan garis dengan pola yang ditemukan tidak akan dicetak jika tidak secara eksplisit dilakukan seperti yang digambarkan. (Setidaknya itu menunjukkan tes saya.) Perhatikan bahwa tidak ada 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).
Janis
1
@ mikeserv; Ah, baiklah. BTW, saya hanya mencoba yang headbukan read; tampaknya hanya sedikit lebih lambat, tapi itu kode terser: { head -1 <##XYZ ; { read <##"" ;} >file4 ;} <largefile >file3.
Janis
1
@ mikeserv; Poin bagus; bukan itu. Tetapi ketika saya mengaktifkan builtin (baru saja selesai dan memeriksa hasilnya) itu angka yang sama, anehnya. (Mungkin beberapa fungsi panggilan overhead dibandingkan dengan membaca?)
Janis
6
{ sed '/XYZ/q' >file1; cat >file2; } <infile

Dengan GNU sedAnda harus menggunakan -usakelar nbuffered. Sebagian besar lainnya sedseharusnya hanya berfungsi.

Untuk membiarkan XYZ keluar ...

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

Coba ini dengan sed GNU:

sed -n -e '1,/XYZ/w file1' -e '/XYZ/,${/XYZ/d;w file2' -e '}' large_file
Cyrus
sumber
Lebih pendek:sed -e '1,/XYZ/{w file1' -e 'd}' large_file > file2
don_crissti
1

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:

  1. Membuang garis yang cocok dengan pola perpecahan:

    perl -ne 'if(/XYZ/){$a=1; next} ; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2
  2. Termasuk garis yang cocok:

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

Atau, cetak ke berbagai pegangan file:

  1. 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 "$_";' f
  2. Termasuk garis yang cocok:

    perl -ne 'BEGIN{open($fh1,">","f1"); open($fh2,">","f2");}
              $a=1 if /XYZ/; $a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
terdon
sumber