Obtener información específica de un archivo de registro

Obtener información específica de un archivo de registro

Quiero obtener algunas partes de mi archivo de registro. Intenté cortar la parte de la solicitud para obtener el usuario, el módulo, la acción, doAjax y ajaxAction.

Por ejemplo, tengo

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

y quiero tener:

[user]|[module]|[action]|[doAjax]|[ajaxAction] 
usery  contacts  form     null     null
userx  customer  null     request  getContacts
userz  meeting   null     date     null

Dónde:

userx --> user  
m=xxx -->module   
a=xxx -->action  
doajax=xxx-->doAjax   
action=xxx-->ajaxAction  

Intenté usar awk, pero para cortar solo la séptima columna donde puedo encontrar mi solicitud con este comando:

awk '{printf $7; next ; }' logfile

Entonces, ¿cómo puedo extraer el usuario, el módulo, la acción, doAjax y ajaxAction después de imprimir solo mi solicitud?

Respuesta1

Una "de una sola línea" de Perl:

$ 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           

El truco básico aquí es buscar cada una de las cadenas que identifican las partes de su URL y, si las encuentra, establecer la variable correspondiente. En cada caso, buscamos el identificador seguido de =(p. ej. m=) y luego a &o el final de la línea (&|$). Debido a que la parte coincidente se coloca entre paréntesis (por ejemplo m=(.+?)), podemos referirnos a ella como $2y eso es lo que se guarda en cada variable.

Si realmente necesita tenerlo |como separador y no se opone al hecho de que hará que la salida sea menos legible, puede usar esto en su lugar:

$ 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

Un enfoque mejor (salida más legible) sería utilizar printfen su lugar:

Respuesta2

Si prefieres hacer esto en awk, puedes hacer lo siguiente. Dividir le permite dividir una cadena con cualquier separador de campo.

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

Esto genera las columnas deseadas.

userx contacts a form
usery customer doajax request
userx meeting doajax date

El paso restante es formatear. Las matrices en awk son asociativas y se pueden indexar con cadenas; consulteaquí. Puedes hacer lo siguiente; aquí, op (abreviatura de salida) se inicializa en nulo. Luego, fijamos 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 

[modificado a]

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 

La salida es la siguiente

userx contacts form null null
usery customer null request getContacts
userx meeting null date null

Respuesta3

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

Resultados

[user]|[module]|[action]|[doAjax]|[ajaxAction]
userx  contacts form     null     null
usery  customer null     request  getContacts
userx  meeting  null     date     null

Explicación

  1. Opciones de Perl:

    -l marcasORS = RS = \n

    -aalmacena los campos en una matriz @Fobtenida al dividir el registro actual /\s+/, por lo que, por ejemplo,$F[0] => $1, $F[1] => $2, ..., $F[-1] => $NF

    -n configura un bucle implícito que lee el archivo de entrada línea por línea Y no genera salida a menos que se solicite.

  2. Bloque COMENZAR:

    Primero imprimimos el encabezado. Luego determinamos el formato dinámicamente según el encabezado. Para cada línea leída, configuramos un hash %h cuyas claves son las cadenas antes de = y los valores son la cadena después de =. La cuerda que se va a mirar está colindante con ? o & a la izquierda y & a la derecha. A continuación, configuramos otro hash %H cuyas claves son versiones renombradas del hash %h. A continuación imprimimos el hash según el formato que calculamos en el bloque BEGIN.

información relacionada