
Ich möchte einige Teile aus meiner Protokolldatei abrufen. Ich habe versucht, den Anforderungsteil auszuschneiden, um Benutzer, Modul, Aktion, doAjax und ajaxAction abzurufen.
Ich habe zum Beispiel
195.xx.x.x - - [25/Apr/2017:09:60:xx +0200] "POST /userx/index.php?m=contacts&a=form&...
192.xx.x.x - - [25/Apr/2017:09:45:xx +0200] "POST /usery/index.php?m=customer&doajax=request&action=getContacts...
197.xx.x.x - - [25/Apr/2017:09:20:xx +0200] "GET /userx/index.php?m=meeting&doajax=date&id=3
und ich möchte haben:
[user]|[module]|[action]|[doAjax]|[ajaxAction]
usery contacts form null null
userx customer null request getContacts
userz meeting null date null
Wo:
userx --> user
m=xxx -->module
a=xxx -->action
doajax=xxx-->doAjax
action=xxx-->ajaxAction
Ich habe versucht, „set“ zu verwenden awk
, aber um nur die 7. Spalte auszuschneiden, in der ich meine Anfrage mit diesem Befehl finden kann:
awk '{printf $7; next ; }' logfile
Wie kann ich also vorgehen, um Benutzer, Modul, Aktion, doAjax und AjaxAction zu extrahieren, um anschließend nur meine Anfrage auszudrucken?
Antwort1
Ein Perl-Einzeiler:
$ perl -lne '
BEGIN{
printf "%-10s%-10s%-10s%-10s%-15s\n", qw([user] [module] [action] [doAjax] [ajaxAction]);
}
$usr = $mde = $act = $doAj = $ajAc = "null";
$usr=$1 if m|\s/([^/]+)/|;
$mde=$1 if /m=(.+?)(&|$)/;
$act=$1 if /a=(.+?)(&|$)/;
$doAj=$1 if /doajax=(.+?)(&|$)/;
$ajAc=$1 if /action=(.+?)(&|$)/;
printf "%-10s%-10s%-10s%-10s%-15s\n", ($usr,$mde,$act,$doAj,$ajAc)' file
[user] [module] [action] [doAjax] [ajaxAction]
userx contacts form null null
usery customer null request getContacts
userx meeting null date null
Der grundlegende Trick besteht darin, nach jedem String zu suchen, der Ihre URL-Teile identifiziert, und, falls gefunden, die entsprechende Variable darauf zu setzen. In jedem Fall suchen wir nach dem Bezeichner, gefolgt von einem =
(zB m=
) und dann entweder einem &
oder dem Zeilenende (&|$)
. Da der übereinstimmende Teil in Klammern gesetzt ist (zB m=(.+?)
), können wir ihn dann als referenzieren $2
und das ist, was in jeder Variable gespeichert wird.
Wenn Sie es unbedingt als Trennzeichen benötigen |
und es Ihnen nichts ausmacht, dass die Ausgabe dadurch schlechter lesbar wird, können Sie stattdessen Folgendes verwenden:
$ perl -lne '
BEGIN{
printf "%s|%s|%s|%s|%s\n", qw([user] [module] [action] [doAjax] [ajaxAction]);
}
$usr = $mde = $act = $doAj = $ajAc = "null";
$usr=$1 if m|\s/([^/]+)/|;
$mde=$1 if /m=(.+?)(&|$)/;
$act=$1 if /a=(.+?)(&|$)/;
$doAj=$1 if /doajax=(.+?)(&|$)/;
$ajAc=$1 if /action=(.+?)(&|$)/;
print join "|", ($usr,$mde,$act,$doAj,$ajAc)' file
[user]|[module]|[action]|[doAjax]|[ajaxAction]
userx|contacts|form|null|null
usery|customer|null|request|getContacts
userx|meeting|null|date|null
Ein besserer Ansatz (mit besser lesbarer Ausgabe) wäre printf
stattdessen die Verwendung von:
Antwort2
Wenn Sie dies lieber in awk tun möchten, können Sie wie folgt vorgehen: Mit Split können Sie einen String mit einem beliebigen Feldtrennzeichen teilen.
awk '{split($7,a,"/"); split(a[3],b,"m="); split(b[2],c,"&"); split(c[2],d,"="); print a[2], c[1], d[1], d[2] }' logfile
Dadurch werden die gewünschten Spalten generiert.
userx contacts a form
usery customer doajax request
userx meeting doajax date
Der verbleibende Schritt ist das Formatieren. Arrays in awk sind assoziativ und können mit Strings indiziert werden - sieheHier. Sie können Folgendes tun; hier wird op (kurz für output) auf null initialisiert. Dann setzen wir op[d[1]]=d[2]
.
awk '{split($7,a,"/"); split(a[3],b,"m="); split(b[2],c,"&"); split(c[2],d,"="); op["a"]="null"; op["doajax"]="null"; op["ajaxaction"]="null"; op[d[1]]=d[2];print a[2], c[1], op["a"], op["doajax"], op["ajaxaction"] }' junk.txt
[geändert zu]
awk '{split($7,a,"/"); split(a[3],b,"m="); split(b[2],c,"&"); split(c[2],d,"="); op["a"]="null"; op["doajax"]="null"; op["action"]="null"; op[d[1]]=d[2]; split(c[3],f,"="); split(f[2],g,"."); op[f[1]]=g[1]; print a[2], c[1], op["a"], op["doajax"], op["action"] }' junk.txt
Die Ausgabe ist wie folgt
userx contacts form null null
usery customer null request getContacts
userx meeting null date null
Antwort3
perl -lane '
BEGIN {
print $H = join "|", map { s/.*/[$&]/r } @H = qw/user module action doAjax ajaxAction/;
pos($H) = 0;
push(@pos, pos($H)-$p), $p=pos($H) while $H =~ /\[/g;
$fmt = join "", map { "\%-${_}s" } @pos[1..$#pos], length($H)-$p;
}
my(%h, %H) = $F[-1] =~ /[?&]\K([^=]+)=([^&]+)/g;
@H{@H} = ($F[-1] =~ m|^/([^/]+)|, @h{qw/m a doajax action/});
print sprintf $fmt, map { $H{$_} // "null" } @H;
' logfile
Ergebnisse
[user]|[module]|[action]|[doAjax]|[ajaxAction]
userx contacts form null null
usery customer null request getContacts
userx meeting null date null
Erläuterung
Perl-Optionen:
-l
machtORS = RS = \n
-a
speichert Felder in einem Array@F
, das durch Aufteilen des aktuellen Datensatzes erhalten wurde/\s+/
, also z. B.$F[0] => $1, $F[1] => $2, ..., $F[-1] => $NF
-n
richtet eine implizite Schleife ein, die die Eingabedatei zeilenweise liest UND keine Ausgabe erzeugt, sofern diese nicht angefordert wird.BEGIN-Block:
Zuerst drucken wir den Header. Dann bestimmen wir das Format dynamisch basierend auf dem Header. Für jede gelesene Zeile richten wir einen Hash %h ein, dessen Schlüssel die Zeichenfolgen vor = und deren Werte die Zeichenfolgen nach = sind. Die zu betrachtende Zeichenfolge wird links von ? oder & und rechts von & begrenzt. Als Nächstes richten wir einen weiteren Hash %H ein, dessen Schlüssel umbenannte Versionen des %h-Hashes sind. Als Nächstes drucken wir den Hash basierend auf dem Format, das wir im BEGIN-Block berechnet haben.