
Ich habe mehrere Einträge, die eineEreignisin einer sehr großen Protokolldatei, sagen wirEin HolzklotzIch möchte zwei Dinge tun mit demEreignisEinträge in der Logdatei:
- Zählen Sie die Anzahl der Vorkommen jedes solchen Eintrags. (Dies ist keine zwingende Voraussetzung, wäre aber wünschenswert.)
- Extrahieren Sie die eigentlichen Einträge in eine separate Datei und studieren Sie sie später.
Ein typischer Ereigniseintrag würde wie folgt aussehen und zwischen den Einträgen weitere Texte enthalten. Im folgenden Beispiel gibt es also zweiEreignisEinträge, wobei der erste zwei DataChangeEntry
Nutzlasten 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 ==== DataChangeEntry
Zeilen in einem Ereigniseintrag variabel sein kann. Sie kann auch vollständig fehlen, was auf eine leere Ereignisnutzlast hinweist und einen Fehlerzustand darstellt. Auch diesen Fall möchte ich unbedingt abfangen.
Da sich in diesem Fall die Ausgabe von entry über mehrere Zeilen erstreckt, komme ich mit einfachem Vanilla-Grep nicht weit. Ich suche daher Expertenrat.
PS:
- Lassen Sie mich meine Anforderung genauer erläutern. Ich möchte den gesamten oben gezeigten Textblock wörtlich erfassen und optional die Anzahl der gefundenen Instanzen solcher Blöcke zählen. Die Option zum Zählen der Anzahl der Instanzen ist gut, aber keine zwingende Voraussetzung.
- Wenn die Lösung des Problems die Verwendung von awk ist, möchte ich die awk-Datei speichern und erneut verwenden. Bitte erwähnen Sie daher auch die Schritte zur Ausführung des Skripts. Ich kenne Regex und grep, aber ich bin nicht mit sed und/oder awk vertraut.
Antwort1
Ich hoffe, das würde es tun. Ereignisse werden in events
eine Datei geschrieben. Und Nachrichten werden an die Standardausgabe gesendet.
Speichern Sie diese Datei beispielsweise in myprogram.awk:
#!/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 events
im Arbeitsverzeichnis überprüfen.
Antwort2
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
NR
ist die aktuelle Zeilennummer inawk
.RS="]]"
setzt den Datensatztrenner (was eine „Zeile“ definiert) auf]]
. Das bedeutet, dass jeder Eintrag von als einzelne Zeile behandelt wirdawk
.{print > NR".entry"}
: Dadurch wird die aktuelle Zeile (Eintrag) in eine Datei mit dem Namen gedruckt[LineNumber].entry
.1.entry
Sie enthält also die 1.,2.entry
die 2. usw.END{print NR" entries"}
: Der END-Block wird ausgeführt, nachdem die gesamte Eingabedatei verarbeitet wurde. DaherNR
wird an diesem Punkt die Anzahl der verarbeiteten Einträge angezeigt.
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"
Anschließend führen Sie das Skript (vorausgesetzt, es wird aufgerufen foo.sh
und befindet sich in Ihrem $PATH) mit der Zieldatei als Argument aus:
foo.sh file
Sie können auch die Namen der Ausgabedateien anpassen. Um die Dateien beispielsweise [date].[entry number].[entry]
folgendermaßen zu benennen:
#!/usr/bin/env bash
date=$(date +%Y%m%d)
awk '{print > d"."NR".entry"}END{print NR" entries"}' RS="]]" d="$date" "$1"
Das Obige setzt voraus, dass Ihre Protokolldatei ausschließlich aus „Ereignis“-Einträgen besteht. Wenn das nicht der Fall ist und Sie andere Zeilen haben können und diese Zeilen ignoriert werden sollen, 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