Ich muss einen Text aus dem Protokoll auswählen und speichern, dass das Feld eine Spalte zur neuen Datei enthält.
Im Folgenden finden Sie beispielsweise das Protokollformat
[Mon Dec 07] [error] [client 10.0.0.65] [id "981004"] [file "sample"] [line "84"] [hostname "test"] [uri "/login"] [unique_id "VmVddAo"]
[Mon Dec 07] [error] [client 10.0.0.65] [file "sample"] [line "47"] [id "960015"] [rev "1"] [msg "Request Missing an Accept Header"] [severity "NOTICE"][ver "OWASP_CRS/2.2.9"] [maturity "9"] [accuracy "9"] [tag "MISSING_HEADER_ACCEPT"] [tag "WASCTC/WASC-21"] [tag "OWASP_TOP_10/A7"] [tag "PCI/6.5.10"] [hostname "test"] [uri "/home"] [unique_id "VmVddQo"]
Möchten Sie die Ausgabe wie unten drucken
[Mon Dec 07] [id "981004"] [uri "/login"]
[Mon Dec 07] [id "960015"] [uri "/home"]
Ich habe awk verwendet, um als spaltenweise zu drucken
grep "Mon Dec 07" filename | sed '/\[[a-zA-Z]/\t&/g' | awk -F'\t' '{print $5}'
But i got the below output
[id "981004"]
[file "sample"]
Because the column are found on different places, for example
[id "981004"] in the 4th column
[id "960015"] in the 6th column
Wie man den Wert mit like erhält, ist die ID als Schlüssel und innerhalb der doppelten Anführungszeichen der Wert für diesen Schlüssel. Nach Auswahl aller Werte muss es in einer neuen Datei (csv) als Spalte gespeichert werden.
Danke vrs & Mirosław Zalewski
#!/bin/bash
search=$1
log=$2
regexp="s/(\[$search[^]]*\]).+(\[id[^]]*\]).+(\[uri[^]]*\]).+/\1 \2 \3/p"
sed -rn "$regexp" $2
&
perl -n -e '$,=" "; @groups = $_ =~ m/(\[.*?\]).*(\[(?:id|uri).*?\]).*((?-2)).*/ ; print @groups, "\n"' /path/to/log/file.log
Beide arbeiteten ...
2 Antworten
Sie können dies mit einem Skript wie dem folgenden tun:
#!/bin/bash
search=$1
log=$2
regexp="s/(\[$search[^]]*\]).+(\[id[^]]*\]).+(\[uri[^]]*\]).+/\1 \2 \3/p"
sed -rn "$regexp" $2
Sie können dieses Programm in einer Datei speichern (z. B. script.sh
), es ausführbar machen (chmod +x script.sh
) und $ ./script.sh "Mon Dec 07" log.txt
ausführen.
Dies ist, was das Skript tut:
- Weist dem Skript das erste Argument der Variablen
$search
(Text, mit dem Sie Zeilen abgleichen möchten) zu, das zweite der Variablen$log
(Name einer Protokolldatei). - Erstellt einen regulären Ausdruck für
sed
-
(...)
bedeutet Gruppierung -
\[some text\]
bedeutetsome text
in eckigen Klammern (sie werden mit Baclslashes versehen) -
[^...]
bedeutet ein beliebiges Zeichen außer...
, d. H.[^]]
bedeutet ein beliebiges Zeichen außer einer schließenden eckigen Klammer (die für die Regex-Beendigung benötigt wird) -
.+
bedeutet eine beliebige positive Anzahl von Zeichen -
\1
bedeutet, dass wir Text aus der ersten Gruppe verwenden müssen (siehe erste Aufzählung) - Die Optionen von
sed
-rn
bedeuten, dass der Standarddruck jeder Zeile und die Verwendung erweiterter regulärer Ausdrücke unterdrückt werden.
-
- Verwenden Sie den regulären Ausdruck mit sed in Ihrer Protokolldatei
log.txt
Ich hoffe, das hilft.
Dies ist eine schnelle und schmutzige einzeilige Lösung in Perl:
perl -n -e '$,=" "; @groups = $_ =~ m/(\[.*?\]).*(\[(?:id|uri).*?\]).*((?-2)).*/ ; print @groups, "\n"' /path/to/log/file.log
In diesem Fall wird davon ausgegangen, dass das erste Feld das Datum enthält. Es ist keine bestimmte Reihenfolge der Felder id
und uri
erforderlich, es werden sie jedoch in der Reihenfolge gedruckt, in der sie in der Datei angezeigt werden.
Eine andere, flexiblere, etwas weniger schmutzige und vielzeilige Lösung in Perl:
%seeked = map { $_ => 1 } qw(id uri unique_id severity msg);
while (<>) {
my $string = $_;
my $closing = 1;
while ( $closing != -1 ) {
$closing = index($string, "]");
$field = substr($string, 0, $closing+1);
$field =~ s/^\s+|\s+$//g;
$string = substr($string, $closing + 1);
my @content = split(/ /, $field, 4);
if (scalar @content == 3 and $field !~ m/"/) {
print $field . " ";
next;
}
if ($seeked{ substr($content[0], 1) }) {
print $field . " ";
}
}
print "\n";
}
Um es zu verwenden, kopieren Sie diesen Code, fügen Sie ihn in eine Datei ein und speichern Sie ihn als whatever.pl
. Geben Sie dann in der Shell Folgendes ein:
perl /path/to/whatever.pl /path/to/log/file.log
In der ersten Codezeile deklarieren Sie in Klammern direkt nach qw
Felder, die gedruckt werden sollen. Ändern Sie diese Zeile, wenn Sie andere Felder oder einige dieser Felder nicht möchten.
Diese Lösung kann immer noch keine Felder in der vorgegebenen Reihenfolge drucken und keine Zeichenfolge anstelle des fehlenden optionalen Felds drucken. Dazu benötigen Sie einen Parser. Fügen Sie zuerst Felder in eine Datenstruktur ein und drucken Sie dann am Zeilenende erkannte Felder.
Darüber hinaus lautet der Code zur "Datumserkennung" Very Wrong ™. Es wird geprüft, ob das Feld genau zwei Leerzeichen enthält und kein doppeltes Anführungszeichen enthält. Wenn beide Bedingungen erfüllt sind, wird davon ausgegangen, dass das Feld Datum ist, und es wird gedruckt. Die richtige Lösung würde prüfen, ob die Zeichenfolge ein gültiges Datum darstellt. Dies kann durch Date :: Manip CPAN Modul, muss aber separat installiert werden.
Wenn Sie beliebige Felder in beliebiger Reihenfolge drucken möchten, müssen Sie einen benutzerdefinierten (?) Parser für dieses Dateiformat schreiben. Dies sollte nicht zu schwierig sein, aber es gibt einige Fallstricke, nach denen Sie suchen müssen:
- Datumsfeld hat impliziten Schlüssel
- Typfeld hat impliziten Schlüssel
- Zumindest das Feld "client" gibt keinen Wert an
- Viele Felder sind optional
- Einige Felder können mehrmals vorkommen (mindestens "Tag").
Ich denke nicht, dass CSV ein geeignetes Format für diese Art von Daten wäre. Es ist am besten für zweidimensionale Datenstrukturen. Da Ihre Daten optionale Felder und Felder zum Speichern von Listen enthalten, ist etwas Flexibleres - wie XML oder JSON - besser.
Nebenbei bemerkt, es scheint, dass jeder, der diese Protokolle schreibt, dachte, dass es einfach sein wird, sie zu lesen. Ich würde empfehlen, die Dokumentation / den Support des Anbieters zu überprüfen, um festzustellen, ob eine bestimmte Lösung im Sinn ist.