
Wir haben eine Protokolldatei, die ich oft live mit tail verfolge und mit grep nach den Zeilen filtere, die mich interessieren. Die Zeilen enthalten jedoch viele Daten, die mich nicht immer interessieren, aber sie waren für mich schwer zu analysieren, sodass ich nur die Teile der Zeile sehe, die ich haben möchte. Das Format jedes Zeileneintrags besteht hauptsächlich aus einer Liste von Tags und den Daten (die manchmal Leerzeichen enthalten), die in Anführungszeichen eingeschlossen sind. Hier sind einige Beispiele (bereinigter) Protokollzeilen:
2017:11:29-11:29:56 filter-1 httpproxy[3194]: id="0001" severity="info" sys="SecureWeb" sub="http" name="http access" action="pass" method="CONNECT" srcip="10.11.12.13" dstip="14.3.1.4" user="" group="" ad_domain="" statuscode="200" cached="0" profile="REF_HttPro1234 (Campus2)" filteraction="REF_HttStu (Allow Policy)" size="6518" request="0x915a3e00" url="https://website.net/" referer="" error="" authtime="0" dnstime="1" cattime="73" avscantime="0" fullreqtime="61576999" device="0" auth="6" ua="" exceptions="" category="9998" reputation="unverified" categoryname="Uncategorized" country="United States" application="krux" app-id="826"
2017:11:29-11:29:56 filter-1 httpproxy[3194]: id="0001" severity="info" sys="SecureWeb" sub="http" name="http access" action="pass" method="GET" srcip="10.13.14.15" dstip="154.6.75.10" user="" group="" ad_domain="" statuscode="200" cached="0" profile="REF_HttPro1235 (Campus1)" filteraction="REF_HttStu (Allow Policy)" size="3161" request="0x6b4d5610" url="http://host.com/mini_banner.png" referer="http://www.web.com/computers.htm" error="" authtime="0" dnstime="0" cattime="64" avscantime="848" fullreqtime="50046" device="0" auth="6" ua="Mozilla/5.0 (X11; CrOS x86_64 9765.85.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.123 Safari/537.36" exceptions="" category="111" reputation="trusted" categoryname="Education/Reference" sandbox="-" content-type="image/png"
Zu beachten ist, dass nicht alle Tags in jeder Zeile vorkommen. Beispielsweise sind „application“ und „app-id“ in der ersten Zeile vorhanden, aber nicht in der zweiten.
Wenn ich die obigen Zeilen als Beispieleingabe verwende, würde ich als Ausgabe beispielsweise nur die Tags srcip, categoryname und url in dieser Reihenfolge anzeigen. Die gewünschte Ausgabe würde ungefähr so aussehen:
10.11.12.13 Uncategorized https://website.net/
10.13.14.15 Education/Reference http://host.com/mini_banner.png
Ich suche nach einer Lösung, die leicht anpassbar ist, sodass ich spontan anpassen kann, welche Tags angezeigt werden.
Antwort1
Ihre Daten sind hoch strukturiert, daSchlüssel="Wert", Sie können also mit gnu awk ein kleines Shell-Skript schreiben, das als Argument eine Liste von Schlüsselnamen verwendet und nur diese Werte ausgibt. Beispiel myscript
:
#!/bin/bash
awk -v lhs="$*" '
BEGIN{ FPAT = "[a-z-]*=\"[^\"]*\""
nwant = split(lhs,want)
}
{ for(i=1;i<=NF;i++){
start = match($i,/([a-z-]*)="([^"]*)"/,a)
key[a[1]] = a[2]
}
for(i=1;i<=nwant;i++){printf "%s ",key[want[i]]; key[want[i]] = ""}
printf "\n"
}'
die Sie als aufrufen myscript srcip categoryname url
. Dies setzt die awk-Variable lhs
auf die Argumente als einzelne Zeichenfolge, die want
am Anfang in Arrays aufgeteilt werden. Die Zeilen werden von awk in Felder aufgeteilt, die dem Muster entsprechenSchlüssel="Wert"durch Verwendung der integrierten FPAT
Variable.
In jeder Zeile teilen wir jedes Feld match()
in zwei Gruppen auf, für den Schlüssel und für den Teil in Anführungszeichen. Diese werden von awk in ein Array eingefügt a
und wir speichern sie in einem assoziativen Array, key
das durch die Schlüsselzeichenfolge indiziert wird.
Dann drucken wir für jeden gewünschten Schlüssel den Wert und löschen ihn für die nächste Zeile (falls diese Zeile diesen Schlüssel nicht hat). Dies setzt natürlich voraus, dass alle Daten die erforderliche Struktur haben und Änderungen erforderlich sind, um (") innerhalb des Werts oder Schlüssel mit nicht alphabetischen Zeichen zu verarbeiten.
Versionen von gnu awk (gawk) vor 4.0 verfügen nicht über die FPAT
integrierte Funktion zum Aufteilen der Zeile in Felder, die einem Muster entsprechen. Sie müssen dies daher selbst tun:
#!/bin/bash
awk -v lhs="$*" '
BEGIN{ nwant = split(lhs,want) }
{ input = $0
while(match(input,"[a-z-]*=\"[^\"]*\"")>0){
field = substr(input,RSTART,RLENGTH)
input = substr(input,RSTART+RLENGTH)
start = match(field,/([a-z-]*)="([^"]*)"/,a)
key[a[1]] = a[2]
}
for(i=1;i<=nwant;i++){printf "%s ",key[want[i]]; key[want[i]] = ""}
printf "\n"
}'
Natürlich könnten Sie die beiden Match-Aufrufe zu einem kombinieren, aber dies zeigt den Unterschied zum Original.
Antwort2
Verwenden von (POSIX-konform) sed
...
sed 's/.* srcip="\([^"]*\)" .* url="\([^"]*\)" .* categoryname="\([^"]*\)" .*/\1 \3 \2/' logfile
Hier gibt es nichts Ausgefallenes, suchen Sie einfach die Schlüssel und umgeben Sie die Werte mit Klammern \(..\)
, damit sie als Rückverweise verwendet werden können. Dann ersetzen wir die Zeichenfolge nur durch die Rückverweise, durch Leerzeichen getrennt und nach Ihren Anforderungen sortiert: \1 \3 \2
.
Ausgabe:
10.11.12.13 Uncategorized https://website.net/
10.13.14.15 Education/Reference http://host.com/mini_banner.png
Wenn die Protokolle Zeichenfolgen enthalten, die nicht über alle dieser Schlüssel verfügen, können Sie Folgendes verwenden:
sed -n 's/.* srcip="\([^"]*\)" .* url="\([^"]*\)" .* categoryname="\([^"]*\)" .*/\1 \3 \2/p' logfile
Dadurch werden nur Zeilen gedruckt, die dem Muster entsprechen.
Und wenn Sie diese natürlich im Streaming-Modus verwenden möchten, entfernen Sie einfach den Dateinamen und tun Sie[something sending logs to stdout] | sed ...