2017-06-30 113 views
5

Kayıt dosyam var (bir müşteriden). 18 Gigs. Dosyanın tüm içeriği 1 satırda. Dosyayı logstash içinde okumak istiyorum. Ama bellek yüzünden problemim var. Dosya satır satır okunur ama maalesef hepsi 1 satırda. DenedimHer bir}, 1 satırdan oluşan büyük (12 GB) bir} n ile değiştirin.

}\n ile değiştirerek } o logstash onu (dosya basit bir json biçimi, hiçbir iç içe nesneler vardır) işleyebilir yüzden tek satırda, bölme her json olmasını istedik hatları içine dosyası bölme

sed -i 's/}/}\n/g' NonPROD.log.backup 

Ama sed Ancak öldürdüğü için - bellek nedeniyle de varsayalım. Bunu nasıl çözebilirim? sed dosyasının başka satırlar kullanarak dosyaları satırlara göre değiştirmesine izin verebilir miyim? Varsayılan olarak sed satır satırını okudum biliyorum.

+2

Bir akış moda dosyanızı işlemek için 'jq' kullanarak düşünebilir = çıkış, zamanlama bekleniyor var. – chepner

cevap

6

şu kabuk içinde bulunan tek işlevselliği kullanır:

Eğer logstash (aşağıda keyfi bir örnek olarak 14321 kullanarak) bir TCP bağlantı noktasından okumaya yapılandırdıysanız
#!/bin/bash 

# as long as there exists another } in the file, read up to it... 
while IFS= read -r -d '}' piece; do 
    # ...and print that content followed by '}' and a newline. 
    printf '%s}\n' "$piece" 
done 

# print any trailing content after the last } 
[[ $piece ]] && printf '%s\n' "$piece" 

, sen thescript <NonPROD.log.backup >"/dev/tcp/127.0.0.1/14321" veya benzeri ve çalıştırabilir Orada - orijinal girdi dosyanızın diskte bulunan boşluğunu ikiye katlamak zorunda kalmadan, şu ana kadar verilmiş olan diğer cevaplar gereklidir. Sen tr akıtarak yapabilirsiniz

1

Yapabilirsin:

  1. Bölünmüş tüm dosyaları sed 's/}/}\n/g' x*
  2. split -b 1m file.log
  3. Süreci kullanılarak 1M parçalarını söylemek dosya ... ve tek bir parça geri bunları birleştirmek için sed çıktısını yönlendirmek

Bunun sakıncası iki kat depolama alanıdır.

+1

Çıktıyı diske serileştirmek isterse ne yaparsa yapsın dönüştürün, orijinal girdiden daha büyük bir çıktı üretir - sed -i 'geçici bir çıktı yazıyorsa, çıktı çıkması gerekiyor * yere *. Sadece çıktı eşit veya daha küçükse gerçekten yerinde dönüşüm yapabilir. Orijinal dosyayı "split" yapıldıktan hemen sonra sildiyseniz, double + 1mb'yi (artı yeni satırların kendilerinin büyüklüğü) geçmekten kaçınabilirsiniz. –

+0

(...iyi - "sadece" biraz güçlüydü - Linux, bellek eşlemeli bir dosyadaki belirli blokların geriye dönük olarak silinmesine ve uygun bir dosya sistemi kullanıldıysa seyrek hale getirilmesine izin veren bazı uzantılara sahiptir, ancak bu oldukça büyük bir durumdur). –

2

, ardından her satırın sonunda tekrar bileziklerle koydu:

$ cat NonPROD.log.backup | tr '}' '\n' | sed 's/$/}/' > tmp$$ 
$ wc -l NonPROD.log.backup tmp$$ 
    0 NonPROD.log.backup 
    43 tmp10528 
    43 total 

(Testim dosya sadece 43 kademelere sahipti.)

+0

Basit, ancak (zaten orada değilse) dosyanın sonuna ek bir '}' ekleme dışında çalışır. Kediyi boru hattından çıkarmayı öneriyorum -

+0

Bıktım - zamanla% 1'lik bir düşüş var. – Jack

+1

Yup. Program, bir FIFO ile önden arkaya okumaya gerek duymak yerine, aranabilir bir dosya tanıtıcısı verildiğinde paralel hale getirilebilecek "sort" gibi bir şey olduğunda çok daha büyük bir fark olacaktır. ('cat file |' '' file ''' '' '' '' '' '' '' '' '' '' '' '' '' '' doğrudan erişim sağlamaz, ancak sadece '' cat '' dan akışlı çıkış sağlar. Bir başka örnek de, "wc -c" - gerçek bir dosya tanıtıcısıyla, dosyanın ne kadar büyük olursa olsun, sabit bir zamanda tamamlanabilmesi, bir FIFO ile koşulsuz olarak her şeyi okuması gerekir. –

0

RT için GNU awk ile fold

$ fold -w 1000 long_line_file | sed 's/}/}\n\n/g' | tr -s '\n' 
+0

Gereksinim duymadığınız yerlerde yeni satırlar alırsınız. –

3

başka alternatif: Şu anda yayınlanan tüm çözümleri denemeye karar verdik

$ printf 'abc}def}ghi\n' | awk -v RS='}' -v ORS='}\n' 'NR>1{print p} {p=$0} END{printf "%s",p}' 
abc} 
def} 
ghi 

: Diğer awks ile

$ printf 'abc}def}ghi\n' | awk -v RS='}' '{ORS=(RT?"}\n":"")}1' 
abc} 
def} 
ghi 

Bir giriş filini kullanarak işlev ve yürütme süresi için Bu komutla oluşturulan e:

awk 'BEGIN{for(i=1;i<=1000000;i++)printf "foo}"; print "foo"}' > file1m 

ve burada ne var yani:

time awk -v RS='}' '{ORS=(RT?"}\n":"")}1' file1m 

beklenen var çıkışı, zamanlaması =

:

1) awk (her ikisi awk komut yukarıdaki benzer sonuçlar) vardı

real 0m0.608s 
user 0m0.561s 
sys  0m0.045s 

2) shell loop:

$ cat tst.sh 
#!/bin/bash 

# as long as there exists another } in the file, read up to it... 
while IFS= read -r -d '}' piece; do 
    # ...and print that content followed by '}' and a newline. 
    printf '%s}\n' "$piece" 
done 

# print any trailing content after the last } 
[[ $piece ]] && printf '%s\n' "$piece" 

$ time ./tst.sh < file1m 

real 1m52.152s 
user 1m18.233s 
sys  0m32.604s 

3) tr+sed = çıkış zamanlama beklenen var:

$ time tr '}' '\n' < file1m | sed 's/$/}/' 

eklendi beklenen çıktı (dosyanın sonuna istenmeyen }) üretmemiştir, zamanlama =

real 0m0.577s 
user 0m0.468s 
sys  0m0.078s 

bir çimdik ile bu son istenmeyen } kaldırmak için:

$ time tr '}' '\n' < file1m | sed 's/$/}/; $s/}//' 

real 0m0.718s 
user 0m0.670s 
sys  0m0.108s 

4) fold+sed+tr:

$ time fold -w 1000 file1m | sed 's/}/}\n\n/g' | tr -s '\n' 

çıkış zamanlama beklenen var =

real 0m0.811s 
user 0m1.137s 
sys  0m0.076s 

5) split+sed+cat:

$ cat tst2.sh 
mkdir tmp$$ 
pwd="$(pwd)" 
cd "tmp$$" 
split -b 1m "${pwd}/${1}" 
sed -i 's/}/}\n/g' x* 
cat x* 
rm -f x* 
cd "$pwd" 
rmdir tmp$$ 

$ time ./tst2.sh file1m 

real 0m0.983s 
user 0m0.685s 
sys  0m0.167s