Önceki Satır'a Satır nasıl eklenir?


9

Ayrıştırılması ve analiz edilmesi gereken bir Günlük dosyası var. Dosyada aşağıdakine benzer bir şey var:

Dosya:

20141101 server contain dump
20141101 server contain nothing
    {uekdmsam ikdas 

jwdjamc ksadkek} ssfjddkc * kdlsdl
sddsfd jfkdfk 
20141101 server contain dump

Yukarıdaki senaryoya dayanarak, başlangıç ​​satırının tarih içerip içermediğini veya önceki satıra eklemem gereken Numara'yı kontrol etmem gerekiyor.

Çıktı dosyası:

20141101 server contain dump
20141101 server contain nothing {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdl sddsfd jfkdfk 
20141101 server contain dump

Yanıtlar:


11

perlNegatif önden okuma kullanan bir sürüm :

$ perl -0pe 's/\n(?!([0-9]{8}|$))//g' test.txt
20141101 server contain dump
20141101 server contain nothing    {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdlsddsfd jfkdfk
20141101 server contain dump

-0boyunca eşlenmesi için düzenli ifade sağlar tüm dosya , ve \n(?!([0-9]{8}|$))(ile hattının bir değil 8 basamağı takip yeni satır ya da son anlamına gelir, bir negatif ileri yönlü olan -0, dosyanın sonuna olacaktır).


@terdon, son satırsonu kaydedilecek şekilde güncellendi.
muru

Güzel bir! Seni oylar ama korkarım zaten vardı :)
terdon

Hayır, -0NUL ile sınırlandırılmış kayıtlar içinse. -0777Tüm dosyayı bellekte silmek için kullanın (burada gerek yoktur).
Stéphane Chazelas

@ StéphaneChazelas Peki Perl'i yeni dosyayla eşleştirmenin en iyi yolu, tüm dosyayı okumaktan başka ne?
muru

Dosyayı satır satır işleyen diğer yanıtlara bakın.
Stéphane Chazelas

5

İle biraz kolay olabilir sed

sed -e ':1 ; N ; $!b1' -e 's/\n\+\( *[^0-9]\)/\1/g'
  • ilk bölüm 1 uzun satıra :1;N;$!b1bölünmüş dosyadaki tüm satırları toplar\n

  • ikinci kısım şerit newline sembolü ise, aralarında olası boşluklar bulunan rakam olmayan sembolü izledi.

Bellek sınırlamasını önlemek için (özellikle büyük dosyalar için) şunları kullanabilirsiniz:

sed -e '1{h;d}' -e '1!{/^[0-9]/!{H;d};/^[0-9]/x;$G}' -e 's/\n\+\( *[^0-9]\)/\1/g'

Ya da zor bir sedsenaryoyu unutun ve o yılın2

tr '\n2' ' \n' | sed -e '1!s/^/2/' -e 1{/^$/d} -e $a

Güzel, +1. Nasıl çalıştığına dair bir açıklama ekleyebilir misiniz lütfen?
terdon

1
Ah. Güzel. Hep tr '\n' $'\a' | sed $'s/\a\a*\( *[^0-9]\)/\1/g' | tr $'\a' '\n'kendim yaparım .
mirabilos

Maalesef, GNUism olan sed (1) 'te POSIX TEMEL DÜZENLİ ANLATIM S olmayan şeyleri kullandığınız için aşağıya inmek zorundasınız.
mirabilos

1
@Costas, bu GNU grep'in kılavuz sayfası. POSIX BRE özellikleri var . ERE BRE eşdeğer +olduğunu \{1,\}. [\n]taşınabilir de değil. \n\{1,\}POSIX olur.
Stéphane Chazelas

1
Ayrıca, bir etiketten sonra başka bir komutunuz olamaz. POSIX seds : 1;xiçindeki 1;xetiketi tanımlamaktır . Yani gerekir: sed -e :1 -e 'N;$!b1' -e 's/\n\{1,\}\( *[^0-9]\)/\1/g'. Ayrıca, birçok seduygulamanın desen alanlarının boyutu üzerinde küçük bir sınırlama olduğunu unutmayın (POSIX yalnızca 10 x LINE_MAX IIRC'yi garanti eder).
Stéphane Chazelas

5

Bunun bir yolu:

 $ perl -lne 's/^/\n/ if $.>1 && /^\d+/; printf "%s",$_' file
 20141101 server contain dump
 20141101 server contain nothing    {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdlsddsfd jfkdfk 
 20141101 server contain dump

Ancak, bu son satırsonu da kaldırır. Tekrar eklemek için şunu kullanın:

$ { perl -lne 's/^/\n/ if $.>1 && /^\d+/; printf "%s",$_' file; echo; } > new

açıklama

-lHer bir tane ekleyin da yeni satır sondaki (ve kaldıracaktır printkullandığım yüzden çağrı printfyerine. Daha sonra, sayı (mevcut hat başlar eğer /^\d+/) ve akım çizgisi sayısı (birden yüksekse $.>1, bu bir ekstra eklemekten kaçının gereklidir başında boş hat), bir ekleme \nsatırın başına kadar. printfher satırı yazdırır.


Alternatif olarak, tüm \nkarakterleri olarak \0değiştirebilir, ardından \0bir sayı dizesinden hemen önce olan karakterleri \ntekrar olarak değiştirebilirsiniz:

$ tr '\n' '\0' < file | perl -pe 's/\0\d+ |$/\n$&/g' | tr -d '\0'
20141101 server contain dump
20141101 server contain nothing    {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdlsddsfd jfkdfk 
20141101 server contain dump

Yalnızca 8 rakamlı dizelerle eşleşmesi için bunun yerine şunu kullanın:

$ tr '\n' '\0' < file | perl -pe 's/\0\d{8} |$/\n$&/g' | tr -d '\0'

İlk argüman printfolan biçimi . Kullanımprintf "%s", $_
Stéphane Chazelas

@ StéphaneChazelas neden? Demek istediğim, daha temiz olduğunu ve belki de daha kolay anlaşıldığını biliyorum ama bunun koruyacağı bir tehlike var mı?
terdon

Evet, giriş% karakter içeriyorsa yanlış ve potansiyel olarak tehlikelidir. %10000000000sÖrneğin ile bir giriş deneyin .
Stéphane Chazelas

C'de, bu çok iyi bilinen çok kötü bir uygulama ve güvenlik açığı kaynağıdır. İle perl, echo %.10000000000f | perl -ne printfmakinemi dizlerinin üstüne getiriyor.
Stéphane Chazelas

@ StéphaneChazelas vay, evet. Benim de. O zaman yeterince adil, cevap düzenlendi ve teşekkürler.
terdon

3

Bunu kullanarak yapmayı deneyin :

#!/usr/bin/awk -f

{
    # if the current line begins with 8 digits followed by
    # 'nothing' OR the current line doesn't start with 8 digits
    if (/^[0-9]{8}.*nothing/ || !/^[0-9]{8}/) {
        # print current line without newline
        printf "%s", $0
        # feeding a 'state' variable
        weird=1
    }
    else {
        # if last line was treated in the 'if' statement
        if (weird==1) {
            printf "\n%s", $0
            weird=0
        }
        else {
            print # print the current line
        }
    }
}
END{
    print # add a newline when there's no more line to treat
}

Kullanmak için:

chmod +x script.awk
./script.awk file.txt

2

Kullanmanın bir başka basit yolu (diğer cevabımdan) ve terdon algoritması:

awk 'NR>1 && /^[0-9]{8}/{printf "%s","\n"$0;next}{printf "%s",$0}END{print}' file

ITYM END{print ""}. Alternatif:awk -v ORS= 'NR>1 && /^[0-9]{8}/{print "\n"};1;END{print "\n"}'
Stéphane Chazelas


0

Le program en bash:

while read LINE
do
    if [[ $LINE =~ ^[0-9]{8} ]]
    then
        echo -ne "\n${LINE} "
    else
        echo -n "${LINE} "
    fi
done < file.txt

tek satırlı formda:

while read L; do if [[ $L =~ ^[0-9]{8} ]]; then echo -ne "\n${L} "; else echo -n "${L} "; fi done < file.txt

Ters eğik çizgi koruma ( read -r) ve önde gelen boşluklarla (hemen IFS=sonra while) çözüm:

while IFS= read -r LINE
do
    if [[ $LINE =~ ^[0-9]{8} ]]
    then
        echo
        echo -nE "\n${LINE} "
    else
        echo -nE "${LINE} "
    fi
done < file.txt

tek satırlık form:

while IFS= read -r L; do if [[ $L =~ ^[0-9]{8} ]]; then echo; echo -nE "${L} "; else echo -nE "${L} "; fi done < file.text

Satır, örneğin, ters eğik çizgi ve bir n. Ayrıca boşlukları da keser. Ama bunu mkshyapmak için kullanabilirsiniz :while IFS= read -r L; do [[ $L = [0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]* ]] && print; print -nr -- "$L"; done; print
mirabilos

Tabii ki her şey algoritması için değil, görev tarafından sağlanan gereksinimler için çözüm. Tabii ki nihai çözüm, genellikle Gerçek Hayatta olduğu gibi bir bakışta daha karmaşık ve daha az okunabilir olacaktır :)
rook

Kabul ediyorum, ama özellikle gerçek metni kukla metinle değiştirmeleri durumunda OP ☺ hakkında çok fazla varsaymamanın zor yolunu öğrendim.
mirabilos

0
[shyam@localhost ~]$ perl -lne 's/^/\n/ if $.>1 && /^\d+/; printf "%s",$_' appendDateText.txt

işe yarayacak

i/p:
##06/12/2016 20:30 Test Test Test
##TestTest
##06/12/2019 20:30 abbs  abcbcb abcbc
##06/11/2016 20:30 test test
##i123312331233123312331233123312331233123312331233Test
## 06/12/2016 20:30 abc

o/p:
##06/12/2016 20:30 Test Test TestTestTest
##06/12/2019 20:30 abbs  abcbcb abcbc
##06/11/2016 20:30 test ##testi123312331233123312331233123312331233123312331233Test
06/12/2016 20:30 abc vi appendDateText.txt 
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.