Sed alternatif untuk pencarian dan penggantian pada baris yang sangat panjang

9

Saya memiliki file yang dihasilkan oleh program yang tidak menaruh baris baru di akhir catatan. Saya ingin meletakkan baris baru di antara catatan, dan saya bisa melakukannya dengan skrip sed sederhana:

sed -e 's/}{/}\n{/g'

Masalahnya adalah bahwa file input berukuran beberapa gigabytes, dan oleh karena itu jalur input ke sed panjangnya beberapa GB. sed mencoba untuk menahan garis dalam memori, yang tidak berfungsi dalam kasus ini. Saya mencoba --unbufferedopsi itu, tetapi sepertinya membuatnya lebih lambat dan tidak membiarkannya selesai dengan benar.

Tom Panning
sumber
Apakah mungkin untuk mengunggah file input contoh ke suatu tempat bagi kami untuk mencoba beberapa ide?
mkc
3
Mungkin pertama-tama Anda bisa menggunakan truntuk menerjemahkan }ke dalam \ndan kemudian menggunakan seduntuk menambahkan }di akhir setiap baris? Seperti ini:tr '}' '\n' < your_file.txt| sed 's/$/}/'
user43791
Apakah menambahkan baris baru di akhir file sama sekali membantu? Seperti:printf "\n" >> file
pengasuh
1
@Ketan, saya berasumsi menulis file dengan 78 karakter sampah diikuti dengan }{diulang sampai beberapa gigabytes akan cukup.
pengasuh
@nanny - poin bagus - tapi di mana Anda mendapatkan 78? Jika catatan sudah diblokir maka dd if=file cbs=80 conv=unblockakan melakukannya - tetapi jarang sesederhana itu.
mikeserv

Jawaban:

7

Anda dapat menggunakan alat lain yang memungkinkan Anda mengatur pemisah rekaman input. Sebagai contoh

  • Perl

    perl -pe 'BEGIN{ $/="}{" } s/}{/}\n{/g' file
    

    Variabel khusus $/adalah pemisah rekaman input. Mengaturnya untuk }{mendefinisikan garis sebagai berakhiran }{. Dengan begitu Anda dapat mencapai apa yang Anda inginkan tanpa membaca semuanya ke dalam memori.

  • mawk atau gawk

    awk -v RS="}{" -vORS= 'NR > 1 {print "}\n{"}; {print}' file 
    

    Ini ide yang sama. RS="}{"mengatur pemisah rekaman ke }{dan kemudian Anda mencetak }, baris baru, {(kecuali untuk catatan pertama) dan catatan saat ini.

terdon
sumber
3

Perl untuk penyelamatan:

perl -i~ -e ' $/ = \1024;
              while (<>) {
                  print "\n" if $closing and /^{/;
                  undef $closing;
                  s/}{/}\n{/g;
                  print;
                  $closing = 1 if /}$/;
              } ' input1 input2

Pengaturan $/untuk \1024akan membaca file dalam potongan 1024 byte. The $closingvariabel menangani kasus ketika sepotong berakhir di }dan yang berikutnya dimulai dengan {.

choroba
sumber
1
+1, mungkin solusi terbaik; solusi perl / awk lainnya juga berfungsi dengan baik, tetapi bagaimana jika pemisah rekaman pertama terjadi setelah sekitar 17GB senilai karakter?
don_crissti
2

Kamu seharusnya melakukan:

{ <infile tr \} \\n;echo {; } | paste -d'}\n' - /dev/null >outfile

Itu mungkin solusi yang paling efisien.

Yang menempatkan {}untuk melindungi data trailing yang mungkin. Dengan satu trproses lagi Anda dapat menukar itu sekitar dan melakukan baris kosong di kepala {bidang pertama . Suka...

tr {} '}\n'| paste -d{\\0 /dev/null - | tr {}\\n \\n{}

Jadi yang pertama, dengan contoh data don, tidak:

printf '{one}{two}{three}{four}' |
{ tr \} \\n; echo {; }           |
paste -d'}\n' - /dev/null
{one}
{two}
{three}
{four}
{}

... dan yang kedua tidak ...

printf '{one}{two}{three}{four}'      |
tr {} '}\n'| paste -d{\\0 /dev/null - |
tr {}\\n \\n{}
#leading blank
{one}
{two}
{three}
{four}

Tidak ada trailing newline untuk contoh kedua - meskipun ada satu untuk yang pertama.

mikeserv
sumber
0

sedUtilitas mirip Biner disebutbbe

Saya merasa paling mudah untuk tetap dengan sintaks sed-seperti dalam kasus ini.

Saya lebih suka menggunakan bbeutilitas (tersedia melalui instalasi paket {uni, linu} x, eq apt-get). Atau di sini jika Anda salah satu dari kerumunan git, meskipun saya belum secara pribadi memeriksa tautan tertentu.

1. Ini mendukung s/before/after/idiom

Ini adalah "Binary Block Editor", yang mendukung operasi sed-like (antara lain). Ini termasuk s/before/after/idiom substitusi super umum yang Anda butuhkan. Catatan, karena tidak ada garis per se dari bbesudut pandang, tidak ada "global g" di akhir perintah.

Sebagai tes cepat (perhatikan wajib -e):

$ echo hello | bbe -e 's/l/(replaced)/'

menghasilkan:

he(replaced)(replaced)o

2. Dalam kasus spesifik Anda }{untuk }\n{konversi

Jadi jika kita memiliki file besar yang diisi dengan jutaan angka di (katakanlah) format {1}{2}{3}... {1000000}tanpa pengembalian carriage, kita dapat bertukar }{dengan }\n{mudah, dan memiliki semua angka satu per baris.

Ini akan dengan bbeperintah ini :

bbe -e 's/}{/}\n{/'

Seperti yang diuji dalam loop zsh ini, yang kami ambil hanya ekor:

$ for ((num=0; num<1000000; num++)) do; echo -n "{$num}"; done | bbe -e 's/}{/}\n{/' | tail

Yang akan menghasilkan ini:

{999990}
{999991}
{999992}
{999993}
{999994}
{999995}
{999996}
{999997}
{999998}
{999999}

(Tanpa kembalinya kereta belakang tentu saja.)

tgm1024 - Monica dianiaya
sumber