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 XYZ
sedemikian rupa sehingga file1
berisi baris up-to XYZ
dan 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 XYZ
sedemikian rupa sehingga file1
berisi baris up-to XYZ
dan sisa baris di file2
.
Jawaban:
Dengan awk
Anda 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 awk
Program akan mencetak semua baris ke file yang ditentukan oleh variabel out
( {print >out}
). Jika pola XYZ
akan 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 s
membagi file, membuat potongan dengan pre f
ix file
dan n
jumlah menggunakan satu digit, misalnya file0
dll. Perhatikan bahwa menggunakan /regex/
akan dibagi, tetapi tidak termasuk baris yang cocok regex
. Untuk memisahkan dan termasuk pencocokan garis, regex
tambahkan +1
offset:
csplit -sf file -n 1 large_file /XYZ/+1
Ini menciptakan dua file, file0
dan file1
. Jika Anda benar-benar membutuhkannya untuk diberi nama file1
dan file2
Anda selalu dapat menambahkan pola kosong ke csplit
perintah dan menghapus file pertama:
csplit -sf file -n 1 large_file // /XYZ/+1
membuat file0
, file1
dan file2
tetapi file0
kosong sehingga Anda dapat menghapusnya dengan aman:
rm -f file0
Dengan yang modern ksh
inilah varian shell (yaitu tanpa sed
) dari salah satu sed
jawaban berdasarkan di atas:
{ read in <##XYZ ; print "$in" ; cat >file2 ;} <largefile >file1
Dan varian lain ksh
saja (yaitu juga menghilangkan cat
):
{ read in <##XYZ ; print "$in" ; { read <##"" ;} >file2 ;} <largefile >file1
( ksh
Solusi murni tampaknya cukup performan; pada file uji 2,4 GB dibutuhkan 19-21 detik, dibandingkan dengan 39-47 detik dengan pendekatan sed
/ cat
berbasis).
read
dan print
- Anda hanya harus membiarkannya untuk menghasilkan sendiri. Performa menjadi lebih baik jika Anda membangun AST toolkit sepenuhnya dan ksh
menyusun semua builtin - aneh bagi saya yang sed
bukan salah satu dari mereka, sebenarnya. Tetapi dengan hal-hal seperti while <file do
saya kira Anda tidak perlu sed
begitu banyak ...
awk
perform di benchmark Anda? Dan sementara saya cukup yakin ksh
kemungkinan akan selalu memenangkan pertarungan ini, jika Anda menggunakan GNU sed
Anda tidak bersikap adil terhadap sed
- GNU yang -u
nbuffered 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 sed
harus 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 while
loop eksplisit yang saya harapkan akan lebih lambat secara signifikan (tetapi belum dicentang).
head
bukan 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 f
dan dua file output f1
dan f2
:
Membuang garis yang cocok dengan pola perpecahan:
perl -ne 'if(/XYZ/){$a=1; next} ; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2
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:
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
Termasuk garis yang cocok:
perl -ne 'BEGIN{open($fh1,">","f1"); open($fh2,">","f2");}
$a=1 if /XYZ/; $a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
XYZ
jalur dimasukkan dalam output atau tidak?