Çok uzun hatlarda arama ve değiştirme için Sed alternatifi


9

Kayıtların sonuna satırsonu koymayan bir program tarafından oluşturulan dosyalarım var. Kayıtlar arasına satırsonu koymak istiyorum ve bunu basit bir sed komut dosyası ile yapabilirim:

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

Sorun, girdi dosyalarının boyut olarak birden çok gigabayt olması ve bu nedenle sed için kullanılan giriş satırlarının uzunluğunun birden fazla GB olması. sed, bu durumda çalışmayan bir satırı bellekte tutmaya çalışır. --unbufferedSeçeneği denedim , ancak bu daha yavaş görünüyordu ve düzgün bir şekilde bitmesine izin vermedi.


Bazı fikirleri denememiz için bir yere örnek bir girdi dosyası yüklemek mümkün müdür?
mkc

3
Belki önce kullanabilirsiniz trçevirmek }içine \nkullanmak ve daha sonra sedbir ekleme }her satırın sonunda? tr '}' '\n' < your_file.txt| sed 's/$/}/'
Şunun

Dosyanın sonuna yeni satır eklemek hiç yardımcı oluyor mu? Gibi:printf "\n" >> file
dadı

1
@Ketan, 78 }{gigabaytlık bir dosya yazmanın birkaç gigabayt uzunluğa kadar tekrarlanmasının ardından olduğunu varsayıyorum .
dadı

@nanny - iyi bir nokta - ama nereden 78 alabilirim? Kayıtlar zaten engellenmişse dd if=file cbs=80 conv=unblockbunu yapardı - ancak nadiren bu kadar basit.
mikeserv

Yanıtlar:


7

Giriş kayıt ayırıcısını ayarlamanıza izin veren başka bir araç kullanabilirsiniz. Örneğin

  • Perl

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

    Özel değişken $/girdi kayıt ayırıcısıdır. Bunu }{satırlara bitiş olarak tanımlar }{. Bu şekilde, her şeyi belleğe okumadan istediğinizi elde edebilirsiniz.

  • mawk veya gawk

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

    Bu aynı fikir. RS="}{"kayıt ayırıcıyı (ilk kayıt hariç) ve geçerli kayda yeni bir satır }{yazdırır ve sonra yazdırırsınız .}{


3

Kurtarmaya Perl:

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

Ayar $/için \10241024 bayt parçalarını dosyayı okuyacak. $closingDeğişken kolları durumda iken bir yığın uçları }ile bir sonraki başlar {.


1
+1, muhtemelen en iyi çözüm; diğer perl / awk çözümleri de iyi çalışıyor, ancak ilk kayıt ayırıcı yaklaşık 17GB değerinde karakterlerden sonra olursa?
don_crissti

2

Yapmalısın:

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

Muhtemelen en verimli çözümdür.

Bu, {}olası tüm sondaki verileri korumak için a'yı koyar . Bir trişlem daha ile bunu değiştirebilir ve ilk {alanın başında boş bir çizgi yapabilirsiniz . Sevmek...

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

İlk olarak, don'ın örnek verileriyle:

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

... ve ikincisi ...

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

İkinci örnek için bir satırsonu yoktur - ilk örnek için bir tane olsa da.


0

Binary sedbenzeri bir yardımcı programbbe

Bu durumda sed benzeri sözdizimi ile kalmayı en kolay buluyorum.

Ben çok kullanmayı tercih bbe(sizin {uni, linu} x paket kurulumu, denklem yoluyla ulaşılabilir yarar apt-get). Ya da burada git kalabalığından biriyseniz, o bağlantıyı kişisel olarak incelememiş olsam da.

1. s/before/after/Deyimi destekler

Sed-benzeri (diğerlerinin yanı sıra) işlemleri destekleyen bir "İkili Blok Editörü" dür. Bu, ihtiyacınız olan süper ortak s/before/after/ikame deyimini içerir. Unutmayın, kendi bbebakış açısından kendi başına satır olmadığından, komutun sonunda "global g" yoktur.

Hızlı bir test olarak (gerekli olduğuna dikkat edin -e):

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

üretir:

he(replaced)(replaced)o

Kullandığınız özel durumda 2. }{için }\n{dönüşüm

(Diyelim) biçiminde bir milyon numaraları ile dolu büyük bir dosya vardı Yani eğer {1}{2}{3}... {1000000}hayır satırbaşları ile biz alışverişinde olabilir }{ile }\n{kolayca ve tüm numaraları her satıra bir tane var.

Bu şu bbekomutla olur:

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

Biz sadece kuyruğunu kapmak bu zsh döngüde test edildiği gibi:

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

Hangi bunu üretecek:

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

(izleyen arabanın dönüşü olmadan elbette.)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.