Suchen nach einem Textblock mit Teilen, die optional sein können

8

Ich habe mehrere Einträge, die ein Ereignis in einer sehr großen Protokolldatei beschreiben, z . B. A.log . Ich möchte zwei Dinge mit den Ereigniseinträgen in der Protokolldatei tun :

  1. Zählen Sie die Anzahl der Vorkommen jedes solchen Eintrags. (Dies ist keine obligatorische Anforderung, wäre aber schön zu haben.)
  2. Extrahieren Sie die tatsächlichen Einträge in einer separaten Datei und studieren Sie sie später.

Ein typischer Ereigniseintrag sieht wie folgt aus und enthält andere Texte. Im folgenden Beispiel gibt es also zwei Ereigniseinträge , von denen der erste zwei DataChangeEntry Nutzdaten und der zweite eine DataChangeEntry Nutzlast enthält.

    Data control raising event :DataControl@263c015d[[
    #### DataChangeEvent #### on [DataControl name=PatternMatch_LegendTimeAxis, binding=.dynamicRegion1.                         beam_project_PatternMatch_dashboard_LegendTimeAxis_taskflow_LegendTimeAxis_beamDashboardLegendTimeAxisPageDef_beam_project_PatternMatch_dashboard_LegendTimeAxis_taskflow_LegendTimeAxis_beamDashboardLegendTimeAxis_xml_ps_taskflowid.dynamicRegion58.                                                                                                                         beam_project_PatternMatch_view_LegendTimeAxis_taskflow_LegendTimeAxis_beamVizLegendTimeAxisPageDef_beam_project_PatternMatch_view_LegendTimeAxis_taskflow_LegendTimeAxis_beamVizLegendTimeAxis_xml_ps_taskflowid.QueryIterator]
    Filter/Collection Id : 0
    Collection Level     : 0
    Sequence Id             : 616
    ViewSetId            : PatternMatch.LegendTimeAxis_V1_0_SN49
    ==== DataChangeEntry (#1)
    ChangeType           : UPDATE
    KeyPath              : [2014-06-26 06:15:00.0, 0]
    AttributeNames       : [DATAOBJECT_CREATED, COUNTX, QueryName]
    AttributeValues      : [2014-06-26 06:15:00.0, 11, StrAvgCallWaitTimeGreaterThanThreshold]
    AttributeTypes       : [java.sql.Timestamp, java.lang.Integer, java.lang.String,  ]
    ==== DataChangeEntry (#2)
    ChangeType           : UPDATE
    KeyPath              : [2014-06-26 06:15:00.0, 0]
    AttributeNames       : [DATAOBJECT_CREATED, COUNTX, QueryName]
    AttributeValues      : [2014-06-26 06:15:00.0, 9, AverageCallWaitingTimeGreateThanThreshold]
    AttributeTypes       : [java.sql.Timestamp, java.lang.Integer, java.lang.String,  ]

    ]]

someother non useful text
spanning multiple lines 

 Data control raising event :DataControl@263c015d[[
    #### DataChangeEvent #### on [DataControl name=PatternMatch_LegendTimeAxis, binding=.dynamicRegion1.                         beam_project_PatternMatch_dashboard_LegendTimeAxis_taskflow_LegendTimeAxis_beamDashboardLegendTimeAxisPageDef_beam_project_PatternMatch_dashboard_LegendTimeAxis_taskflow_LegendTimeAxis_beamDashboardLegendTimeAxis_xml_ps_taskflowid.dynamicRegion58.                                                                                                                         beam_project_PatternMatch_view_LegendTimeAxis_taskflow_LegendTimeAxis_beamVizLegendTimeAxisPageDef_beam_project_PatternMatch_view_LegendTimeAxis_taskflow_LegendTimeAxis_beamVizLegendTimeAxis_xml_ps_taskflowid.QueryIterator]
    Filter/Collection Id : 0
    Collection Level     : 0
    Sequence Id             : 616
    ViewSetId            : PatternMatch.LegendTimeAxis_V1_0_SN49
    ==== DataChangeEntry (#1)
    ChangeType           : UPDATE
    KeyPath              : [2014-06-26 06:15:00.0, 0]
    AttributeNames       : [DATAOBJECT_CREATED, COUNTX, QueryName]
    AttributeValues      : [2014-06-26 06:15:00.0, 11, StrAvgCallWaitTimeGreaterThanThreshold]
    AttributeTypes       : [java.sql.Timestamp, java.lang.Integer, java.lang.String,  ]

    ]]

Bitte beachten Sie, dass die Anzahl der ==== DataChangeEntryZeilen in einem Ereigniseintrag variabel sein kann. Es kann auch vollständig fehlen, was auf eine Nutzlast leerer Ereignisse hinweist und eine Fehlerbedingung darstellt und definitiv auch diesen Fall erfassen möchte.

Da sich in diesem Fall die Ausgabe des Eintrags über mehrere Zeilen erstreckt, komme ich mit einfachem Vanille-Grep nicht weit. Deshalb suche ich kompetenten Rat.

PS:

  1. Lassen Sie mich meine Anforderungen genauer erläutern. Ich möchte den gesamten oben gezeigten Textblock wörtlich erfassen und optional die Anzahl der Instanzen solcher aufgetretenen Blöcke zählen. Die Option, die Anzahl der Instanzen zu zählen, ist gut, aber keine zwingende Voraussetzung.
  2. Wenn die Lösung des Problems awk verwendet, möchte ich die awk-Datei speichern und wiederverwenden. Erwähnen Sie daher bitte auch die Schritte zum Ausführen des Skripts. Ich kenne Regex und Grep, bin aber nicht mit Sed und / oder Awk vertraut.
Geek
quelle
Beginnen sie immer mit Data control raising event?
LatinSuD
@LatinSuD Ja, es beginnt immer mit dieser Zeichenfolge.
Geek
Ich denke, dies ist ein Job für awk, bei dem eine oder mehrere "Zustandsmaschinen" -Variablen verwendet werden. Sie sollten jedoch weitere Informationen hinzufügen, um Hilfe zu erhalten, z. B. die genauen gesuchten Token und das erwartete Endergebnis.
Didi Kohen
@DavidKohen Ein Ereigniseintrag beginnt mit dem Token "Data Control Raising Event" und endet mit "]]" in einer neuen Zeile. Ich möchte jede solche Ereignisinstanz herausfinden .
Geek
Finden Sie was mit ihnen? Ihren Betrag zählen? Alle drucken? Bitte bearbeiten Sie Ihre Frage und fügen Sie die erwartete Beispielausgabe hinzu (vorzugsweise mit anderen Beispieleingaben).
Didi Kohen

Antworten:

4

Das würde es hoffentlich tun. Ereignisse werden in die eventsDatei aufgenommen. Und Nachrichten gehen an stdout.

Speichern Sie diese Datei in myprogram.awk (zum Beispiel):

#!/usr/bin/awk -f

BEGIN {
   s=0;  ### state. Active when parsing inside an event
   nevent=0;  ### Current event number
   printf "" > "events"
}

# Start of event
/^ *Data control raising event/ {
   s=1;
   dentries=0;
   print "*** Event number: " nevent >> "events"
   nevent++
}

# Standard event line
s==1 {
   print >> "events"
}

# DataChangeEntry line
/^ *==== DataChangeEntry/ {
   dentries ++
}

# End of event
s==1 && /^ *\]\]/ {
   s=0;
   print "" >> "events"
   if(dentries==0){
      print "Warning: Event " nevent " has no Data Entries"
   }
}

END {
   print "Total event count: " nevent
}

Sie können es auf verschiedene Arten aufrufen:

  • myprogram.awk inputfile.txt
  • awk -f myprogram.awk inputfile.txt

Beispielausgabe:

Warning: Event 3 has no Data Entries
Total event count: 3

Sie können alle Ereignisse zusammen in der Datei überprüfen, die eventsim Arbeitsverzeichnis aufgerufen wird .

LatinSuD
quelle
Sie sollten den Ereigniszähler getrennt vom Ereigniskopf erhöhen (oder den Operator vorher haben). Dies führt dazu, dass Kopf- und Fußzeile unterschiedliche Zahlen anzeigen und weniger lesbar sind.
Didi Kohen
@LatinSuD Ich bin nicht mit awk vertraut. Wenn Sie also den Teil hinzufügen können, den ich zum Ausführen des obigen Programms ausführen muss, ist dies sehr hilfreich. Für mich lautet die Eingabedatei A.log .
Geek
Um dieses Skript zu verwenden, ersetzen Sie einfach die Datei inputfile.txt durch Ihren Dateinamen oder entfernen Sie die Katze und die Pipe und setzen Sie Ihren Dateinamen nach dem schließenden einfachen Anführungszeichen.
Didi Kohen
@ DavidKohen Ich möchte dieses Skript speichern. Also, wenn ich das speichere als findEvents.awk. Kann ich es so ausführen : awk -f findEvents.awk A.log?
Geek
Sie könnten, aber Sie sollten nur den Teil in den einfachen Anführungszeichen in dieser Datei speichern.
Didi Kohen
2

Ein sehr einfacher Ansatz wäre

awk '{print > NR".entry"}END{print NR" entries"}' RS="]]" file 

Dadurch wird für jeden Eintrag eine separate Datei erstellt und die Anzahl der gefundenen Einträge in der Standardausgabe gedruckt.

Erläuterung

  • NRist die aktuelle Zeilennummer in awk.
  • RS="]]"setzt das Datensatztrennzeichen (was eine "Linie" definiert) auf ]]. Dies bedeutet, dass jeder Eintrag von als einzelne Zeile von behandelt wird awk.
  • {print > NR".entry"}: Hiermit wird die aktuelle Zeile (Eintrag) in eine aufgerufene Datei gedruckt [LineNumber].entry. Also, 1.entrywird die 1., 2.entrydie zweite und so weiter enthalten.
  • END{print NR" entries"}: Der END-Block wird ausgeführt, nachdem die gesamte Eingabedatei verarbeitet wurde. Daher wird an diesem Punkt NRdie Anzahl der verarbeiteten Einträge angegeben.

Sie können dies als Alias ​​speichern oder wie folgt in ein Skript umwandeln:

#!/usr/bin/env bash
awk '{print > NR".entry"}END{print NR" entries"}' RS="]]" "$1"

Sie würden dann das Skript (vorausgesetzt, es heißt foo.shund befindet sich in Ihrem $ PATH) mit der Zieldatei als Argument ausführen :

foo.sh file

Sie können auch die Namen der Ausgabedateien anpassen. [date].[entry number].[entry]Verwenden Sie stattdessen Folgendes, um die Dateien aufzurufen:

#!/usr/bin/env bash
date=$(date +%Y%m%d)
awk '{print > d"."NR".entry"}END{print NR" entries"}' RS="]]" d="$date" "$1"

Das oben Gesagte setzt voraus, dass Ihre Protokolldatei ausschließlich aus "Ereignis" -Einträgen besteht. Wenn dies nicht der Fall ist und Sie andere Zeilen haben können und diese Zeilen ignoriert werden sollten, verwenden Sie stattdessen Folgendes:

 #!/usr/bin/env bash
date=$(date +%Y%m%d)
awk '{
        if(/\[\[/){a=1; c++;}
        if(/\]\]/){a=0; print > d"."c".entry"}
        if(a==1){print >> d"."c".entry"}
}' d="$date" file 

Oder als Einzeiler:

awk '{if(/\[\[/){a=1; c++;}if(/\]\]/){a=0; print > d"."c".entry"}if(a==1){print >> d"."c".entry"}}' d=$(date +%Y%m%d) file 
terdon
quelle