Bagaimana cara menambahkan Baris ke Baris sebelumnya?


9

Saya memiliki file Log yang perlu diuraikan dan dianalisis. File berisi sesuatu yang mirip seperti di bawah ini:

Mengajukan:

20141101 server contain dump
20141101 server contain nothing
    {uekdmsam ikdas 

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

Berdasarkan skenario di atas, saya harus memeriksa apakah baris awal tidak mengandung tanggal atau Nomor saya harus tambahkan ke baris sebelumnya.

Berkas keluaran:

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

Jawaban:


11

Versi masuk perl, menggunakan kepala pencarian negatif:

$ 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

-0memungkinkan regex dicocokkan di seluruh file , dan \n(?!([0-9]{8}|$))merupakan lookahead negatif, artinya baris baru tidak diikuti oleh 8 digit, atau akhir baris (yang, dengan -0, akan menjadi akhir file).


@terdon, diperbarui untuk menyimpan baris baru terakhir.
muru

Yang bagus! Saya akan mendukung Anda, tetapi saya rasa saya sudah memiliki :)
terdon

Tidak, -0jika untuk catatan yang dibatasi NUL. Gunakan -0777untuk menyeruput seluruh file dalam memori (yang Anda tidak perlu di sini).
Stéphane Chazelas

@ StéphaneChazelas Jadi apa cara terbaik untuk membuat Perl cocok dengan baris baru, selain membaca seluruh file?
muru

Lihat jawaban lain yang memproses file baris demi baris.
Stéphane Chazelas

5

Mungkin sedikit mudah sed

sed -e ':1 ; N ; $!b1' -e 's/\n\+\( *[^0-9]\)/\1/g'
  • bagian pertama :1;N;$!b1kumpulkan semua baris dalam file dibagi dengan \ndalam 1 baris panjang

  • bagian kedua lepaskan simbol baris baru jika mengikuti simbol non-digit dengan spasi yang mungkin di antara simbol tersebut.

Untuk menghindari batasan memori (terutama untuk file besar), Anda dapat menggunakan:

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

Atau lupakan sedskrip yang sulit dan untuk mengingat tahun itu dimulai2

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

Bagus, +1. Bisakah Anda menambahkan penjelasan tentang cara kerjanya?
terdon

1
Ah. Bagus. Saya selalu melakukannya tr '\n' $'\a' | sed $'s/\a\a*\( *[^0-9]\)/\1/g' | tr $'\a' '\n'sendiri.
mirabilos

Maaf, harus downvote meskipun untuk menggunakan hal-hal yang bukan POSIX BASIC REGULAR EXPRESSION S di sed (1) , yang merupakan GNUism.
mirabilos

1
@ Costas, itu halaman manual GNU grep. POS BRE spec ada di sana . Setara dengan BRE +adalah ERE \{1,\}. [\n]juga tidak portabel. \n\{1,\}akan menjadi POSIX.
Stéphane Chazelas

1
Selain itu, Anda tidak dapat memiliki perintah lain setelah label. : 1;xadalah mendefinisikan 1;xlabel pada sed POSIX. Jadi, Anda perlu: sed -e :1 -e 'N;$!b1' -e 's/\n\{1,\}\( *[^0-9]\)/\1/g'. Perhatikan juga bahwa banyak sedimplementasi memiliki batasan kecil pada ukuran ruang pola mereka (POSIX hanya menjamin 10 x LINE_MAX IIRC).
Stéphane Chazelas

5

Salah satu caranya adalah:

 $ 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

Namun, itu juga menghapus baris terakhir. Untuk menambahkannya lagi, gunakan:

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

Penjelasan

The -lakan menghapus trailing baris (dan juga menambahkan satu ke setiap printpanggilan yang mengapa saya menggunakan printfsebagai gantinya. Kemudian, jika baris saat ini dimulai dengan angka ( /^\d+/) dan nomor baris saat ini lebih besar dari satu ( $.>1, ini diperlukan untuk menghindari menambahkan ekstra baris kosong di awal), tambahkan a \nke awal baris. printfMencetak setiap baris.


Atau, Anda dapat mengubah semua \nkarakter menjadi \0, lalu mengubah karakter \0yang tepat sebelum serangkaian angka \nlagi:

$ 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

Untuk membuatnya hanya cocok dengan string 8 angka, gunakan ini sebagai gantinya:

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

Argumen pertama printfadalah format . Useprintf "%s", $_
Stéphane Chazelas

@ StéphaneChazelas mengapa? Maksud saya, saya tahu ini lebih bersih dan mungkin lebih mudah dipahami tetapi apakah ada bahaya yang bisa terlindungi?
terdon

Ya, itu salah dan berpotensi berbahaya jika inputnya mungkin mengandung% karakter. Coba dengan input %10000000000smisalnya.
Stéphane Chazelas

Dalam C, itu adalah praktik yang sangat buruk dan sumber kerentanan yang sangat terkenal. Dengan perl, echo %.10000000000f | perl -ne printfmembawa mesin saya ke lutut.
Stéphane Chazelas

@ StéphaneChazelas wow, ya. Milik saya juga. Cukup adil, jawabannya diedit dan terima kasih.
terdon

3

Coba lakukan ini menggunakan :

#!/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
}

Untuk menggunakannya:

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

2

Cara lain yang paling sederhana (daripada jawaban saya yang lain) menggunakan algoritma dan terdon :

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

Program id bash:

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

dalam bentuk satu baris:

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

Solusi dengan backslashes preserving ( read -r) dan spasi terkemuka (hanya IFS=setelah while):

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

bentuk satu baris:

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

Ini akan rusak jika baris berisi, katakanlah, garis miring terbalik dan sebuah n. Ini juga menghapus spasi. Tetapi Anda dapat menggunakannya mkshuntuk melakukan ini: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

Tentu saja itu bukan untuk algoritma segalanya, tetapi solusi untuk persyaratan yang disediakan oleh tugas. Tentu saja solusi terakhir akan menjadi lebih kompleks dan kurang dapat dibaca secara sepintas seperti yang biasanya terjadi di Real Life :)
rook

Saya setuju, tetapi saya telah belajar cara yang sulit untuk tidak menganggap terlalu banyak tentang OP ☺ terutama jika mereka mengganti teks yang sebenarnya dengan teks dummy.
mirabilos

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

Itu akan bekerja

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 
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.