Grep Match y extracto


10

Tengo un archivo que contiene líneas como

proto=tcp/http  sent=144        rcvd=52 spkt=3 
proto=tcp/https  sent=145        rcvd=52 spkt=3
proto=udp/dns  sent=144        rcvd=52 spkt=3

Necesito extraer el valor de proto que es tcp/http, tcp/https, udp/dns.

Hasta ahora he intentado esto grep -o 'proto=[^/]*/'pero solo he podido extraer el valor como proto=tcp/.



Este es un trabajo para sed, awko perlno grep.
OrangeDog

Respuestas:


1

Asumiendo que esto está relacionado con su pregunta anterior , está yendo por el camino equivocado. En lugar de tratar de juntar fragmentos de guiones que hagan lo que quieras la mayor parte del tiempo y necesiten obtener un guión completamente diferente cada vez que necesites hacer algo lo más mínimo posible, solo crea 1 guión que pueda analizar tu ingrese el archivo en una matriz (a f[]continuación) que asigna sus nombres de campo (etiquetas) a sus valores y luego puede hacer lo que quiera con el resultado, por ejemplo, dado este archivo de entrada de su pregunta anterior:

$ cat file
Feb             3       0:18:51 17.1.1.1                      id=firewall     sn=qasasdasd "time=""2018-02-03"     22:47:55        "UTC""" fw=111.111.111.111       pri=6    c=2644        m=88    "msg=""Connection"      "Opened"""      app=2   n=2437       src=12.1.1.11:49894:X0       dst=4.2.2.2:53:X1       dstMac=42:16:1b:af:8e:e1        proto=udp/dns   sent=83 "rule=""5"      "(LAN->WAN)"""

podemos escribir un script awk que cree una matriz de los valores indexados por sus nombres / etiquetas:

$ cat tst.awk
{
    f["hdDate"] = $1 " " $2
    f["hdTime"] = $3
    f["hdIp"]   = $4
    sub(/^([^[:space:]]+[[:space:]]+){4}/,"")

    while ( match($0,/[^[:space:]]+="?/) ) {
        if ( tag != "" ) {
            val = substr($0,1,RSTART-1)
            gsub(/^[[:space:]]+|("")?[[:space:]]*$/,"",val)
            f[tag] = val
        }

        tag = substr($0,RSTART,RLENGTH-1)
        gsub(/^"|="?$/,"",tag)

        $0 = substr($0,RSTART+RLENGTH)
    }

    val = $0
    gsub(/^[[:space:]]+|("")?[[:space:]]*$/,"",val)
    f[tag] = val
}

y dado que puede hacer lo que quiera con sus datos simplemente haciendo referencia a ellos por los nombres de campo, por ejemplo, usando GNU awk -epara facilitar la mezcla de un script en un archivo con un script de línea de comandos:

$ awk -f tst.awk -e '{for (tag in f) printf "f[%s]=%s\n", tag, f[tag]}' file
f[fw]=111.111.111.111
f[dst]=4.2.2.2:53:X1
f[sn]=qasasdasd
f[hdTime]=0:18:51
f[sent]=83
f[m]=88
f[hdDate]=Feb 3
f[n]=2437
f[app]=2
f[hdIp]=17.1.1.1
f[src]=12.1.1.11:49894:X0
f[c]=2644
f[dstMac]=42:16:1b:af:8e:e1
f[msg]="Connection"      "Opened"
f[rule]="5"      "(LAN->WAN)"
f[proto]=udp/dns
f[id]=firewall
f[time]="2018-02-03"     22:47:55        "UTC"
f[pri]=6

$ awk -f tst.awk -e '{print f["proto"]}' file
udp/dns

$ awk -f tst.awk -e 'f["proto"] ~ /udp/ {print f["sent"], f["src"]}' file
83 12.1.1.11:49894:X0

2
Esto es increíble, muchas gracias :)
user356831

Para este tipo de trabajo, perlpuede ser más fácil de usar.
OrangeDog

1
@OrangeDog, ¿por qué piensas eso? De hecho, me gustaría ver el equivalente en perl si no te importa publicar esa respuesta. Sin embargo, Perl definitivamente no será más fácil de usar si no lo tengo en mi caja y no puedo instalarlo, que es algo con lo que he tenido que lidiar con frecuencia a lo largo de los años. Awk, por otro lado, es una utilidad obligatoria, por lo que siempre está presente en las instalaciones de UNIX, como sed, grep, sort, etc.
Ed Morton

@EdMorton es cierto, aunque nunca he encontrado personalmente una distribución en la que perl no se incluye de forma predeterminada. Los complejos awky los sedscripts generalmente son más simples perlporque es esencialmente un superconjunto de ellos, con características adicionales para tareas comunes.
OrangeDog

@OrangeDog nadie debería escribir una secuencia de comandos sed que sea más complicada s/old/new/gy que sed no sea awk, así que dejemos eso de lado. No estoy de acuerdo con que los scripts de awk complejos sean más simples en perl. Pueden ser más breves, por supuesto, pero la brevedad no es un atributo deseable del software, la concisión lo es, y es extremadamente raro que tengan algún beneficio real, además de que generalmente son mucho más difíciles de leer, por eso la gente publica cosas como zoitz.com / archivos / 13 sobre perl y se refieren a él como un lenguaje de solo escritura, a diferencia de awk. Sin embargo, todavía me gustaría ver un perl equivalente a esto
Ed Morton

13

Con grep -o, tendrá que coincidir exactamente con lo que desea extraer. Como no desea extraer la proto=cadena, no debe coincidir con ella.

Una expresión regular extendida que coincidiría con tcpo udpseguida de una barra y alguna cadena alfanumérica no vacía es

(tcp|udp)/[[:alnum:]]+

Aplicando esto en sus datos:

$ grep -E -o '(tcp|udp)/[[:alnum:]]+' file
tcp/http
tcp/https
udp/dns

Para asegurarnos de que solo hacemos esto en las líneas que comienzan con la cadena proto=:

grep '^proto=' file | grep -E -o '(tcp|udp)/[[:alnum:]]+'

Con sed, eliminar todo antes del primer =y después del primer carácter en blanco:

$ sed 's/^[^=]*=//; s/[[:blank:]].*//' file
tcp/http
tcp/https
udp/dns

Para asegurarse de que solo hacemos esto en las líneas que comienzan con la cadena proto=, puede insertar el mismo paso de preprocesamiento con el grepanterior, o puede usar

sed -n '/^proto=/{ s/^[^=]*=//; s/[[:blank:]].*//; p; }' file

Aquí, suprimimos la salida predeterminada con la -nopción, y luego activamos las sustituciones y una impresión explícita de la línea solo si la línea coincide ^proto=.


Con awk , usando el separador de campo predeterminado, y luego dividiendo el primer campo =e imprimiendo el segundo bit:

$ awk '{ split($1, a, "="); print a[2] }' file
tcp/http
tcp/https
udp/dns

Para asegurarse de que solo hacemos esto en las líneas que comienzan con la cadena proto=, puede insertar el mismo paso de preprocesamiento con el grepanterior, o puede usar

awk '/^proto=/ { split($1, a, "="); print a[2] }' file

10

Si está en GNU grep (para la -Popción), puede usar:

$ grep -oP 'proto=\K[^ ]*' file
tcp/http
tcp/https
udp/dns

Aquí hacemos coincidir la proto=cadena, para asegurarnos de que estamos extrayendo la columna correcta, pero luego la descartamos de la salida con el\K bandera.

Lo anterior supone que las columnas están separadas por espacios. Si las pestañas también son un separador válido, lo usaría \Spara hacer coincidir los caracteres que no son espacios en blanco, por lo que el comando sería:

grep -oP 'proto=\K\S*' file

Si también desea protegerse contra los campos de coincidencia donde proto=hay una subcadena, como a thisisnotaproto=tcp/https, puede agregar un límite de palabra de la siguiente \bmanera:

grep -oP '\bproto=\K\S*' file

1
Puede mejorar eso escribiendo solo grep -oP 'proto=\K\S+'. El proto=tcp/httppuede ir seguida de una pestaña en lugar de espacios, y la \Sdiferencia [^ ]coincidirá con cualquier carácter que no sea espacio.
mosvy

@mosvy: Esa es una buena sugerencia, gracias.
user000001

1
De todos modos, también -oes un GNUismo. -Psolo es compatible con GNU grepsi está construido con soporte PCRE (opcional en tiempo de compilación).
Stéphane Chazelas

6

Utilizando awk:

awk '$1 ~ "proto" { sub(/proto=/, ""); print $1 }' input

$1 ~ "proto" se asegurará de que solo tomemos medidas en líneas con proto de la primera columna

sub(/proto=/, "") removerá proto= de la entrada

print $1 imprime la columna restante


$ awk '$1 ~ "proto" { sub(/proto=/, ""); print $1 }' input
tcp/http
tcp/https
udp/dns

3

Código de golf en las grepsoluciones

grep -Po "..p/[^ ]+" file

o incluso

grep -Po "..p/\S+" file


2

Solo otra grepsolución:

grep -o '[^=/]\+/[^ ]\+' file

Y uno similar con sedimprimir solo el grupo capturado coincidente:

sed -n 's/.*=\([^/]\+\/[^ ]\+\).*/\1/p' file

1

Otro awkenfoque:

$ awk -F'[= ]' '/=(tc|ud)p/{print $2}' file
tcp/http
tcp/https
udp/dns

Eso establecerá el separador de campo de awk en uno =o en un espacio. Luego, si la línea coincide con a =, entonces udotc seguido de unp , imprima el segundo campo.

Otro sedenfoque (no portátil para todas las versiones de sed, pero funciona con GNU sed):

$ sed -En 's/^proto=(\S+).*/\1/p' file 
tcp/http
tcp/https
udp/dns

El -nmedio "no imprime" y -Epermite expresiones regulares extendidas que nos dan \Spara "no espacios en blanco", +para "uno o más" y los paréntesis para capturar. Finalmente, el/p al final hará que sed imprima una línea solo si la operación fue exitosa, por lo que si hubo una coincidencia para el operador de sustitución.

Y, uno perl:

$ perl -nle '/^proto=(\S+)/ && print $1' file 
tcp/http
tcp/https
udp/dns

El -nmedio "lee el archivo de entrada línea por línea y aplica la secuencia de comandos dada por -ea cada línea". El -lañade una nueva línea para cada printllamada (y elimina los saltos de línea que salen de la entrada). El script mismo imprimirá el tramo más largo de caracteres que no sean espacios en blanco encontrados después de a proto=.


1
-Ese está volviendo cada vez más portátil, pero \Sno lo es. [^[:space:]]Es un equivalente más portátil.
Stéphane Chazelas

1

Aquí hay otra solución bastante fácil:

grep -o "[tc,ud]*p\\/.*  "   INPUTFile.txt  |   awk '{print $1}'

Tu grepno coincide con nada. [tc,ud]\*\\/.*busca una aparición de t, o c, o ,o uo d, seguido de un *carácter literal , luego pay una barra invertida. Probablemente quisiste decir grep -Eo '(tc|ud)p/.* ' file | awk '{print $1}'. Pero entonces, si estás usando awk, es posible que también lo haga la cosa entera en awk: awk -F'[= ]' '/(tc|ud)p/{print $2}' file.
terdon

Alguien modificó mi original, había una barra invertida adicional antes de la estrella, que acabo de eliminar, señor.
mkzia

Gracias por editar, pero me temo que solo funciona por casualidad. Como he explicado antes, [tc,ud]psignifica "uno de t, c, ,, uo dseguidas por una p. Por lo tanto, los partidos aquí sólo porque tcptiene cpy udptiene dp. Pero también se correspondería ,po tpetc. También, ahora que tiene el *, que coincidirán ppptambién (el *medios "0 o más", por lo que coincidirá incluso cuando no coincide) usted no quiere una clase de caracteres (. [ ]), lo que quiere es un grupo: (tc|ud)(uso con la -Ebandera de grep.) Además, la .*hace coincidir con toda la línea.
terdon

1
@Jesse_b: Si bien mkzia no es técnicamente un "Nuevo contribuyente", es un usuario inexperto, como lo demuestra el hecho de que no utilizaron el formato de código para su comando. Y sin embargo, fueron lo suficientemente inteligentes como para escribir \*para que el primero *en su comando apareciera como un * y no como una marca de cursiva. Cuando pones el comando en formato de código, hiciste que apareciera el \antes *(lo que provocó que el comando fallara). Cuando edite las publicaciones de otras personas, tenga cuidado de cambiar la apariencia de la publicación de esta manera.
G-Man dice 'reinstalar a Monica' el

@terdon: (1) No, en realidad no coincidirá ppp. Por supuesto que tienes razón que coincidirá ,po  tp- o uucp, ttp, cutp, ductpo d,up.
G-Man dice 'reinstalar a Monica' el


0
cat file| cut -f1 -d' '| cut -f2 -d'='
tcp/http
tcp/https
udp/dns

opciones de corte:

  • -f - campo
  • -d - delimitador
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.