Wie füge ich eine Zeile an eine vorherige Zeile an?

9

Ich habe eine Protokolldatei, die analysiert und analysiert werden muss. Die Datei enthält etwas Ähnliches wie unten:

Datei:

20141101 server contain dump
20141101 server contain nothing
    {uekdmsam ikdas 

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

Basierend auf dem obigen Szenario muss ich überprüfen, ob die Startzeile kein Datum oder keine Nummer enthält, die ich an die vorherige Zeile anhängen muss.

Ausgabedatei:

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

Antworten:

11

Eine Version perlmit negativen Lookaheads:

$ 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

-0Ermöglicht die Anpassung des regulären Ausdrucks über die gesamte Datei und \n(?!([0-9]{8}|$))ist ein negativer Lookahead. Dies bedeutet, dass eine neue Zeile nicht von 8 Ziffern oder dem Ende der Zeile (mit -0der das Ende der Datei ist) gefolgt wird .

muru
quelle
@terdon, aktualisiert, um den letzten Zeilenumbruch zu speichern.
Muru
Schön! Ich würde dich positiv bewerten, aber ich fürchte, ich hatte es bereits :)
terdon
Nein, -0wenn für NUL-getrennte Datensätze. Verwenden Sie -0777diese Option, um die gesamte Datei im Speicher zu schlürfen (was Sie hier nicht benötigen).
Stéphane Chazelas
@ StéphaneChazelas Was ist der beste Weg, um Perl an die neue Zeile anzupassen, außer die gesamte Datei einzulesen?
Muru
Siehe die anderen Antworten, die die Datei zeilenweise verarbeiten.
Stéphane Chazelas
5

Kann ein bisschen einfach sein mit sed

sed -e ':1 ; N ; $!b1' -e 's/\n\+\( *[^0-9]\)/\1/g'
  • Der erste Teil :1;N;$!b1sammelt alle Zeilen in der Datei geteilt durch eine \nlange Zeile

  • Der zweite Teil streift das Zeilenumbruchsymbol, wenn er auf ein nichtstelliges Symbol mit möglichen Zwischenräumen folgt.

Um Speicherbeschränkungen zu vermeiden (insbesondere bei großen Dateien), können Sie Folgendes verwenden:

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

Oder vergessen Sie schwierige sedSkripte und denken Sie daran, dass das Jahr beginnt2

tr '\n2' ' \n' | sed -e '1!s/^/2/' -e 1{/^$/d} -e $a
Costas
quelle
Schön, +1. Könnten Sie bitte eine Erklärung hinzufügen, wie es funktioniert?
Terdon
1
Aw. Nett. Ich mache es immer tr '\n' $'\a' | sed $'s/\a\a*\( *[^0-9]\)/\1/g' | tr $'\a' '\n'selbst.
Mirabilos
Es tut uns leid, aber ich muss für die Verwendung von Dingen, die nicht POSIX BASIC REGULAR EXPRESSION S sind, in sed (1) , einem GNUismus, eine Abwertung vornehmen.
Mirabilos
1
@Costas, das ist die Manpage von GNU grep. POSIX BRE-Spezifikationen sind vorhanden . BRE-Äquivalent von ERE +ist \{1,\}. [\n]ist auch nicht tragbar. \n\{1,\}wäre POSIX.
Stéphane Chazelas
1
Außerdem können Sie nach einem Label keinen weiteren Befehl haben. : 1;xist das 1;xLabel in POSIX seds zu definieren . Sie brauchen also : sed -e :1 -e 'N;$!b1' -e 's/\n\{1,\}\( *[^0-9]\)/\1/g'. Beachten Sie auch, dass viele sedImplementierungen eine kleine Begrenzung der Größe ihres Musterbereichs haben (POSIX garantiert nur 10 x LINE_MAX IIRC).
Stéphane Chazelas
5

Ein Weg wäre:

 $ 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

Dadurch wird jedoch auch die letzte neue Zeile entfernt. Um es erneut hinzuzufügen, verwenden Sie:

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

Erläuterung

Das -lentfernt nachfolgende Zeilenumbrüche (und fügt jedem printAnruf einen hinzu, weshalb ich printfstattdessen verwende. Wenn die aktuelle Zeile mit Nummern ( /^\d+/) beginnt und die aktuelle Zeilennummer größer als eins ist ( $.>1dies ist erforderlich, um das Hinzufügen zusätzlicher Zeilen zu vermeiden) leere Zeile am Anfang), fügen Sie \nam Anfang der Zeile ein hinzu. Das printfdruckt jede Zeile.


Alternativ können Sie alle \nZeichen in \0ändern und dann diejenigen \0, die direkt vor einer Zahlenfolge stehen, in \nerneut ändern :

$ 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

Verwenden Sie stattdessen Folgendes, damit nur Zeichenfolgen mit 8 Zahlen übereinstimmen:

$ tr '\n' '\0' < file | perl -pe 's/\0\d{8} |$/\n$&/g' | tr -d '\0'
terdon
quelle
Das erste Argument dafür printfist das Format . Verwenden Sieprintf "%s", $_
Stéphane Chazelas
@ StéphaneChazelas warum? Ich meine, ich weiß, dass es sauberer und vielleicht leichter zu verstehen ist, aber gibt es eine Gefahr, vor der das schützen würde?
Terdon
Ja, es ist falsch und möglicherweise gefährlich, wenn die Eingabe% Zeichen enthält. Versuchen Sie es mit einer Eingabe mit %10000000000szum Beispiel.
Stéphane Chazelas
In C ist dies eine sehr bekannte Quelle für sehr schlechte Praktiken und Schwachstellen. Mit perl, echo %.10000000000f | perl -ne printfbringt meine Maschine in die Knie.
Stéphane Chazelas
@ StéphaneChazelas wow, ja. Meine auch. Fair genug dann, Antwort bearbeitet und danke.
Terdon
3

Versuchen Sie dies mit :

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

Um es zu benutzen:

chmod +x script.awk
./script.awk file.txt
Gilles Quenot
quelle
2

Ein anderer einfachster Weg (als meine andere Antwort) mit dem Algorithmus von und terdon :

awk 'NR>1 && /^[0-9]{8}/{printf "%s","\n"$0;next}{printf "%s",$0}END{print}' file
Gilles Quenot
quelle
ITYM END{print ""}. Alternative:awk -v ORS= 'NR>1 && /^[0-9]{8}/{print "\n"};1;END{print "\n"}'
Stéphane Chazelas
1
sed -e:t -e '$!N;/\n *[0-9]{6}/!s/\n */ /;tt' -eP\;D
mikeserv
quelle
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

in einzeiliger Form:

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

Lösung mit Backslashes, die ( read -r) und führende Leerzeichen (kurz IFS=danach while) erhalten:

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

einzeiliges Formular:

while IFS= read -r L; do if [[ $L =~ ^[0-9]{8} ]]; then echo; echo -nE "${L} "; else echo -nE "${L} "; fi done < file.text
Turm
quelle
Dies wird unterbrochen, wenn die Zeile beispielsweise einen Backslash und einen enthält n. Außerdem werden Leerzeichen entfernt. Aber Sie können mkshdies tun: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
Natürlich ist es nicht für alles Algorithmus, sondern eine Lösung für die Anforderungen der Aufgabe. Natürlich wird die endgültige Lösung auf einen Blick komplexer und weniger lesbar sein, wie es normalerweise im wirklichen Leben der Fall ist :)
Turm
Ich stimme zu, aber ich habe auf die harte Tour gelernt, nicht zu viel über das OP anzunehmen ☺, insbesondere wenn sie den tatsächlichen Text durch Dummy-Text ersetzen.
Mirabilos
0
[shyam@localhost ~]$ perl -lne 's/^/\n/ if $.>1 && /^\d+/; printf "%s",$_' appendDateText.txt

das wird funktionieren

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 
Shyam Gupta
quelle