No necesita toda la línea, solo la coincidencia de la expresión regular

15

Simplemente necesito obtener la coincidencia de una expresión regular:

$ cat myfile.txt | SOMETHING_HERE "/(\w).+/"

La salida tiene que ser solo lo que coincide, dentro del paréntesis.

No creo que pueda usar grep porque coincide con la línea completa.

Por favor, hágame saber cómo hacer esto.

Alex L
fuente

Respuestas:

12

2 cosas:

  • Según lo indicado por @Rory, necesita la -oopción, por lo que solo se imprime la coincidencia (en lugar de la línea completa)
  • Además, tiene la -Popción de utilizar expresiones regulares de Perl, que incluyen elementos útiles como Mirar hacia adelante (?= ) y Mirar hacia atrás (?<= ) , que buscan partes, pero en realidad no coinciden e imprimen.

Si desea que solo coincida la parte dentro del parensis:

grep -oP '(?<=\/\()\w(?=\).+\/)' myfile.txt

si el archivo contiene la picadura /(a)5667/, grep imprimirá 'a', porque:

  • /(son encontrados por \/\(, pero debido a que están en retrospectiva (?<= ) no se informan
  • acoincide \wy, por lo tanto, se imprime (debido a -o)
  • )5667/se encuentran b < \).+\/, pero debido a que están en el futuro (?= ) no se informan
DrYak
fuente
18

Use la -oopción en grep.

P.ej:

$ echo "foobarbaz" | grep -o 'b[aeiou]r'
bar
Rory
fuente
44
Buena pena ... ¿Tienes alguna idea de cuántas veces sedluché con referencias para hacer eso?
Insyte
10
La opción o para grep / egrep devuelve solo lo que coincide con toda la expresión regular, no solo lo que está en () como él pidió.
Kyle Brandt
1
Sin embargo, eso es algo muy bueno para saber de todos modos :-)
Kyle Brandt el
2
@KyleBrandt: para hacer coincidir solo una parte (por ejemplo, los padres) es posible marcar el resto con una mirada hacia adelante o hacia atrás: (? <=) Y (? =)
DrYak
6
    sed -n "s/^.*\(captureThis\).*$/\1/p"

-n      don't print lines
s       substitute
^.*     matches anything before the captureThis 
\( \)   capture everything between and assign it to \1 
.*$     matches anything after the captureThis 
\1      replace everything with captureThis 
p       print it
Joshua
fuente
4

Si solo desea lo que está entre paréntesis, necesita algo que admita la captura de coincidencias secundarias (Grupos de captura con nombre o numerados). No creo que grep o egrep puedan hacer esto, perl y sed pueden. Por ejemplo, con perl:

Si un archivo llamado foo tiene una línea que es la siguiente:

/adsdds      /

Y lo hace:

perl -nle 'print $1 if /\/(\w).+\//' foo

Se devuelve la letra a. Sin embargo, eso podría no ser lo que quieres. Si nos dice qué está tratando de igualar, podría obtener una mejor ayuda. $ 1 es lo que se capturó en el primer conjunto de paréntesis. $ 2 sería el segundo set, etc.

Kyle Brandt
fuente
Solo estaba tratando de hacer coincidir lo que está entre paréntesis. Parece que pasarlo a un perl o un script php podría ser la respuesta.
Alex L
4

Debido a que etiquetó su pregunta como bash además de shell , hay otra solución además de grep :

Bash tiene su propio motor de expresión regular desde la versión 3.0, utilizando el =~operador, al igual que Perl.

ahora, dado el siguiente código:

#!/bin/bash
DATA="test <Lane>8</Lane>"

if [[ "$DATA" =~ \<Lane\>([[:digit:]]+)\<\/Lane\> ]]; then
        echo $BASH_REMATCH
        echo ${BASH_REMATCH[1]}
fi
  • Tenga en cuenta que debe invocarlo como bashy no solo shpara obtener todas las extensiones
  • $BASH_REMATCH dará la cadena completa como coincide con la expresión regular completa, por lo que <Lane>8</Lane>
  • ${BASH_REMATCH[1]} dará la parte correspondiente al primer grupo, por lo tanto solo 8
DrYak
fuente
Estimado @DrYak, espero que no esté analizando XML con
expresiones
Es aun peor. Estoy analizando una horrible combinación de datos XML y FASTA (que usan el >símbolo para propósitos completamente diferentes) como lo describe el software de alineación de gran escala rápida SANSparallel . Por supuesto, ambos formatos se arrojan entrelazados sin escapar. Por lo tanto, es imposible lanzar alguna biblioteca XML estándar a esto. Y estoy usando Bash regex en este punto del código porque solo necesito extraer un par de datos, y 2 regex hacen el trabajo mucho mejor para mí que escribir un analizador dedicado para este desastre. #LifeInBioinformatics
DrYak
En otras palabras: hay un punto en el que extraer 1 solo número es más fácil de hacer con un regex rathan que bailar todo el tango XML
DrYak
Ja, te tengo! :)
joonas.fi
2

Asumiendo que el archivo contiene:

$ cat file
Text-here>xyz</more text

Y desea los caracteres entre >y </, puede usar:

grep -oP '.*\K(?<=>)\w+(?=<\/)' file
sed -nE 's:^.*>(\w+)</.*$:\1:p' file
awk '{print(gensub("^.*>(\\w+)</.*$","\\1","g"))}' file
perl -nle 'print $1 if />(\w+)<\//' file

Todos imprimirán una cadena "xyz".

Si desea capturar los dígitos de esta línea:

$ cat file
Text-<here>1234</text>-ends

grep -oP '.*\K(?<=>)[0-9]+(?=<\/)' file
sed -E 's:^.*>([0-9]+)</.*$:\1:' file
awk '{print(gensub(".*>([0-9]+)</.*","\\1","g"))}' file
perl -nle 'print $1 if />([0-9]+)<\//' file

Flecha
fuente
Para mí fue crucial darse cuenta de que \ d no funciona con sed. Hay una razón por la que usas [0-9] + allí. :)
user27432
@ user27423 no lo hace, pero las clases de caracteres POSIX ( dolorosa de lectura , lectura agradable ) hacer: echo 'Text-<here>1234</text>-ends' | sed -E 's|.*>([[:digit:]]+)<.*|\1|'. En algunos casos (por ejemplo, [0-9]vs. [[:digit:]]) no ayudan a la legibilidad, en otros creo que lo hacen (por ejemplo, [ \t\n\r\f\v]vs. [:space:]).
Samuel Harmer
0

Esto logrará lo que está solicitando, pero no creo que sea lo que realmente quiere. Puse el .*frente de la expresión regular para comer cualquier cosa antes del partido, pero esa es una operación codiciosa, por lo que solo coincide con el penúltimo \wpersonaje de la cadena.

Tenga en cuenta que necesita escapar de los parens y el +.

sed 's/.*\(\w\).\+/\1/' myfile.txt
Chad Huneycutt
fuente