¿Cómo imprimir un patrón de expresión regular coincidente usando awk?

109

Utilizando awk , necesito encontrar una palabra en un archivo que coincida con un patrón de expresión regular.

Yo solamente quiero imprimir la palabra que concordaba con el patrón.

Entonces, si en la línea, tengo:

xxx yyy zzz

Y patrón:

/yyy/

Solo quiero obtener:

yyy

EDITAR: gracias a kurumi logré escribir algo como esto:

awk '{
        for(i=1; i<=NF; i++) {
                tmp=match($i, /[0-9]..?.?[^A-Za-z0-9]/)
                if(tmp) {
                        print $i
                }
        }
}' $1

y esto es lo que necesitaba :) ¡muchas gracias!

marverix
fuente
1
@maxtaldykin ¿Podría mover su propia respuesta de la pregunta a una respuesta separada, por favor?
Kenorb
2
No es necesario que lo haga tmp=match($i, /regexp);if(tmp){}, solo debería poder hacerlo if(tmp ~ $i){}porque ~significa "coincide con la expresión regular".
JustinCB

Respuestas:

148

Este es el muy básico

awk '/pattern/{ print $0 }' file

solicite awkbuscar patternusing //, luego imprima la línea, que por defecto se llama registro, denotado por $ 0. Al menos lee la documentación .

Si solo desea imprimir la palabra coincidente.

awk '{for(i=1;i<=NF;i++){ if($i=="yyy"){print $i} } }' file
Kurumi
fuente
49
Dado que printes la acción predeterminada: awk '/pattern/' fileserá suficiente.
Johnsyweb
18
@Johnsyweb, sí, conozco este hecho. Para un principiante como marverix, está destinado a ser más visual.
Kurumi
21
No dudo de tu conocimiento. Sin embargo, la información puede ser útil para otras personas que encuentren esta respuesta.
Johnsyweb
2
NB: @marverix tendrá que hacer un poco más de tarea para que el for-loop funcione si (a) "yyy" es una expresión regular y no una cadena recta y (b) si ese "yyy" no coincide con un campo completo dentro un registro.
Johnsyweb
8
No lo sería $i=="yyy"; sería $i ~ /yyy/para una expresión regular.
JustinCB
118

Parece que está intentando emular el grep -ocomportamiento de GNU . Esto hará que siempre que solo desee la primera coincidencia en cada línea:

awk 'match($0, /regex/) {
    print substr($0, RSTART, RLENGTH)
}
' file

Aquí hay un ejemplo, usando GNU awk implementación ():

awk 'match($0, /a.t/) {
    print substr($0, RSTART, RLENGTH)
}
' /usr/share/dict/words | head
act
act
act
act
aft
ant
apt
art
art
art

Lea acerca de match, substr, RSTARTyRLENGTH en el awkmanual.

Después de eso, es posible que desee extender esto para tratar con múltiples coincidencias en la misma línea.

Johnsyweb
fuente
NB: Para responder a la última parte, todos los constructos necesarios están en la respuesta de Kurumi y en la mía.
Johnsyweb
Gran respuesta. Solo me gustaría una explicación aquí porque soy vago. ¡Pero por eso estoy usando AWK!
lukas.pukenis
¿Qué sucede si quiero hacer algo con el resultado de la coincidencia, excepto imprimirlo? Por ejemplo, quiero agregar todas las coincidencias en una matriz.
Evya2005
@ evya2005: Simplemente puede reemplazar la llamada de impresión de Ron con la asignación que necesita.
Johnsyweb
No está funcionando para mi. solo trabajo de impresión. ¿puedes mostrarme un ejemplo?
Evya2005
36

gawk puede obtener la parte correspondiente de cada línea usando esto como acción:

{ if (match($0,/your regexp/,m)) print m[0] }

match (cadena, regexp [, matriz]) Si hay una matriz, se borra y luego el elemento cero de la matriz se establece en la parte completa de la cadena que coincide con la expresión regular. Si regexp contiene paréntesis, los elementos de matriz indexados con números enteros se establecen para contener la parte de la cadena que coincide con la subexpresión entre paréntesis correspondiente. http://www.gnu.org/software/gawk/manual/gawk.html#String-Functions

royas
fuente
13

Si solo está interesado en la última línea de entrada y espera encontrar solo una coincidencia (por ejemplo, una parte de la línea de resumen de un comando de shell), también puede probar este código muy compacto, adoptado de Cómo imprimir coincidencias de expresiones regulares usando `awk`? :

$ echo "xxx yyy zzz" | awk '{match($0,"yyy",a)}END{print a[0]}'
yyy

O la versión más compleja con un resultado parcial:

$ echo "xxx=a yyy=b zzz=c" | awk '{match($0,"yyy=([^ ]+)",a)}END{print a[1]}'
b

Advertencia: la awk match()función con tres argumentos solo existe en gawk, no enmawk

Aquí hay otra buena solución que usa una expresión regular de búsqueda hacia atrás en greplugar de awk. Esta solución tiene requisitos más bajos para su instalación:

$ echo "xxx=a yyy=b zzz=c" | grep -Po '(?<=yyy=)[^ ]+'
b
Daniel Alder
fuente
¿Por qué agregaste "tail -n1"? Esto debería funcionar bien sin él, ¿no?
Arthur Accioly
1
@ArthurAccioly Correcto. Usé el término para extraer el tiempo promedio de ida y vuelta de una llamada de ping, de ahí proviene. gracioso que tardó 4 años en descubrirlo;)
Daniel Alder
12

Si Perl es una opción, puede probar esto:

perl -lne 'print $1 if /(regex)/' file

Para implementar la coincidencia que no distingue entre mayúsculas y minúsculas, agregue el imodificador

perl -lne 'print $1 if /(regex)/i' file

Para imprimir todo DESPUÉS del partido:

perl -lne 'if ($found){print} else{if (/regex(.*)/){print $1; $found++}}' textfile

Para imprimir el partido y todo después del partido:

perl -lne 'if ($found){print} else{if (/(regex.*)/){print $1; $found++}}' textfile
Chris Koknat
fuente
3

Usar sed también puede resultar elegante en esta situación. Ejemplo (reemplace la línea con el grupo coincidente "yyy" de la línea):

$ cat testfile
xxx yyy zzz
yyy xxx zzz
$ cat testfile | sed -r 's#^.*(yyy).*$#\1#g'
yyy
yyy

Página del manual correspondiente: https://www.gnu.org/software/sed/manual/sed.html#Back_002dreferences-and-Subexpressions

Konrad Brodzik
fuente
Para no gnu sed, la solución es algo como esto:sed -n 's/^.*\(yyy\).*$/\1/gp' < testfile
Grigory Entin
1
@GrigoryEntin - bsd sed funciona bien con la respuesta original. El modificador de expresiones regulares extendido compatible con POSIX es -E, pero en FreeBSD al menos -r es lo mismo que -E (-r agregado en 2010). De todos modos, intente con -E (gnu sed agregó -E en 4.3)
Juan
3

Fuera del tema, esto también se puede hacer usando grep, simplemente publíquelo aquí en caso de que alguien esté buscando una solución grep

echo 'xxx yyy zzze ' | grep -oE 'yyy'
Zeus
fuente
Una forma sencilla de agarrarlo incluso con expresiones regulares. Exactamente lo que necesitaba. ¡Gracias!
Marquesina
Esto funciona para mí; Mi caso es como: echo "web_port = 8080, shutdown_port = 8005" | grep -oE "web_port = [0-9] +" # return 8080
Robb Tsang
0

Si sabe en qué columna está el texto / patrón que está buscando (por ejemplo, "yyy"), puede verificar esa columna específica para ver si coincide e imprimirla.

Por ejemplo, dado un archivo con el siguiente contenido (llamado asdf.txt )

xxx yyy zzz

para imprimir solo la segunda columna si coincide con el patrón "yyy", puede hacer algo como esto:

awk '$2 ~ /yyy/ {print $2}' asdf.txt

Tenga en cuenta que esto también coincidirá básicamente con cualquier línea donde la segunda columna tenga un "yyy", como estos:

xxx yyyz zzz
xxx zyyyz
kimbo
fuente