2009-11-14 13 views
87

Veo muchos ejemplos y páginas man sobre cómo hacer cosas como buscar y reemplazar usando sed, awk o gawk.cómo usar sed, awk o gawk para imprimir solo lo que coincide?

Pero en mi caso, tengo una expresión regular que quiero ejecutar contra un archivo de texto para extraer un valor específico. No quiero buscar y reemplazar. Esto se llama desde bash. Usemos un ejemplo:

Ejemplo de expresiones regulares:

.*abc([0-9]+)xyz.* 

Ejemplo de archivo de entrada:

a 
b 
c 
abc12345xyz 
a 
b 
c 

Tan simple como suena, no puedo encontrar la manera de llamar sed/awk/gawk correctamente . Lo que estaba esperando a hacer, es de dentro de mi escritura del golpe tienen:

myvalue=$(sed <...something...> input.txt) 

cosas que he intentado incluyen:

sed -e 's/.*([0-9]).*/\\1/g' example.txt # extracts the entire input file 
sed -n 's/.*([0-9]).*/\\1/g' example.txt # extracts nothing 
+5

Wow ... la gente votó esta pregunta abajo -1? ¿Es realmente tan inapropiado de una pregunta? –

+0

Parece perfectamente apropiado, usar Regex y potentes utilidades de línea de comandos como sed/awk o cualquier editor como vi, emacs o teco puede ser más como programar que simplemente usar alguna aplicación antigua. IMO esto pertenece a SO más que SU. – Dereleased

+0

Quizás fue rechazado porque en su forma inicial no definió claramente algunos de sus requisitos. Todavía no lo hace, a menos que lea los comentarios del OP a las respuestas (incluida la que eliminé cuando las cosas se pusieron en forma de pera). – pavium

Respuesta

38

Mi sed (Mac OS X) no funcionó con +.Probé * lugar y añadí p etiqueta para el partido de impresión:

sed -n 's/^.*abc\([0-9]*\)xyz.*$/\1/p' example.txt 

para hacer coincidir al menos un carácter numérico sin +, me gustaría utilizar:

sed -n 's/^.*abc\([0-9][0-9]*\)xyz.*$/\1/p' example.txt 
+0

Gracias, esto funcionó para mí también una vez que utilicé * en lugar de +. –

+2

... y la opción "p" para imprimir la coincidencia, que tampoco conocía. Gracias de nuevo. –

+2

Tuve que escapar del '+' y luego funcionó para mí: 'sed -n 's /^.* abc \ ([0-9] \ + \) xyz. * $/\ 1/p'' –

15

utilizo perl para hacer esto más fácil para mí. p.ej.

perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/' 

Esto va en Perl, la opción -n instruye Perl para leer en una línea a la vez de STDIN y ejecutar el código. La opción -e especifica las instrucciones para ejecutar.

La instrucción ejecuta una expresión regular en la línea leída, y si coincide imprime los contenidos del primer conjunto de bloqueos ($1).

Puede hacer esto con varios nombres de archivo al final también. p.ej.

perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/' example1.txt example2.txt

+0

Gracias, pero no tenemos acceso a Perl, por lo que estaba preguntando sobre sed/awk/gawk. –

1

Si desea seleccionar las líneas a continuación tira a las partes que no desee:

egrep 'abc[0-9]+xyz' inputFile | sed -e 's/^.*abc//' -e 's/xyz.*$//' 

Básicamente selecciona las líneas que desea con egrep y luego utiliza sed a quitarse la bits antes y después del número.

Esto se puede ver en acción aquí:

pax> echo 'a 
b 
c 
abc12345xyz 
a 
b 
c' | egrep 'abc[0-9]+xyz' | sed -e 's/^.*abc//' -e 's/xyz.*$//' 
12345 
pax> 

Actualización:, obviamente, si situación real es más compleja, necesitará el RE para mí modificado. Por ejemplo, si siempre tuviera un solo número enterrado dentro cero o más no-numéricos al comienzo y al final:

egrep '[^0-9]*[0-9]+[^0-9]*$' inputFile | sed -e 's/^[^0-9]*//' -e 's/[^0-9]*$//' 
+0

Interesante ... ¿Entonces no hay una forma simple de aplicar una expresión regular compleja y obtener solo lo que está en la sección (...)? Porque mientras veo lo que hiciste aquí primero con grep y luego con sed, nuestra situación real es mucho más compleja que soltar "abc" y "xyz". Se usa la expresión regular porque pueden aparecer muchos textos diferentes en cualquier lado del texto que me gustaría extraer. –

+0

Estoy seguro de que * es * una mejor forma si los RE son realmente complejos. Quizás si proporcionó algunos ejemplos más o una descripción más detallada, podríamos ajustar nuestras respuestas para que se ajusten. – paxdiablo

-3

Para awk. Me gustaría utilizar el siguiente script:

/.*abc([0-9]+)xyz.*/ { 
      print $0; 
      next; 
      } 
      { 
      /* default, do nothing */ 
      } 
+0

que obtiene un comportamiento similar al grep ... – dmckee

+0

Esto no genera el valor numérico '([0-9 +])', esto genera la línea completa. –

-3
gawk '/.*abc([0-9]+)xyz.*/' file 
+2

Esto no parece funcionar. Imprime toda la línea en lugar de la coincidencia. –

+0

en su archivo de entrada de muestra, ese patrón es la línea completa. ¿¿¿derecho??? si sabe que el patrón va a estar en un campo específico: use $ 1, $ 2, etc., por ejemplo, gawk '$ 1 ~ /.*abc([0-9]+)xyz.*/'archivo – ghostdog74

5

Si su versión de grep lo admite que podría utilizar la opción de imprimir -oúnica la porción de cualquier línea que coincide con su expresión regular.

Si no, entonces aquí es lo mejor sed pude llegar a:

sed -e '/[0-9]/!d' -e 's/^[^0-9]*//' -e 's/[^0-9]*$//' 

... que borra/salta sin dígitos y, para el resto de líneas, elimina todos los caracteres iniciales y finales no sea un dígito . (Solo estoy adivinando que tu intención es extraer el número de cada línea que contiene uno).

El problema con algo como:.

sed -e 's/.*\([0-9]*\).*/&/' 

.... o

sed -e 's/.*\([0-9]*\).*/\1/' 

... es que sólo es compatible con sed partido "codiciosos" ... así que la primera se * empareja el resto de la línea. A menos que podamos usar una clase de caracteres negada para lograr una coincidencia no codiciosa ... o una versión de sed con Perl compatible u otras extensiones a sus expresiones regulares, no podemos extraer una coincidencia de patrón precisa con el espacio de patrones (un línea).

+0

Puede simplemente combinar dos de sus comandos 'sed' de esta manera:' sed -n' s/[^ 0-9] * \ ([0-9] \ + \). */\ 1/p'' –

+0

Anteriormente no sabía acerca de la opción -o en grep. Bueno saber. Pero imprime todo el partido, no el "(...)". Por lo tanto, si coincide en "abc ([[: digit:]] +) xyz", obtendrá los "abc" y "xyz", así como los dígitos. –

-1

puede hacerlo con la cáscara

while read -r line 
do 
    case "$line" in 
     *abc*[0-9]*xyz*) 
      t="${line##abc}" 
      echo "num is ${t%%xyz}";; 
    esac 
done <"file" 
2

Perl es la sintaxis más limpia, pero si usted no tiene Perl (no siempre allí, entiendo), entonces la única manera de utilizar gawk y componentes de una expresión regular es usar la función Gensub.

gawk '/abc[0-9]+xyz/ { print gensub(/.*([0-9]+).*/,"\\1","g"); }' < file 

salida del archivo de entrada de la muestra será

12345 

Nota:. Funciones predefinidas gensub reemplaza toda la expresión regular (entre la //), por lo que necesita para poner el * antes y después de la ([ 0-9] +) para deshacerse del texto antes y después del número en la sustitución.

+2

Una solución inteligente y viable si necesita (o desea) usar gawk. Usted notó esto, pero para ser claro: non-GNU awk no tiene gensub(), y por lo tanto no es compatible con esto. – cincodenada

+0

¡Agradable! Sin embargo, puede ser mejor utilizar 'match()' para acceder a los grupos capturados. Ver [mi respuesta] (http://stackoverflow.com/a/39075261/1983854) para esto. – fedorqui

28

Puede usar sed para hacer esto

sed -rn 's/.*abc([0-9]+)xyz.*/\1/gp' 
  • -n no publique la línea resultante
  • -r esto hace que sea por lo que no tiene el escape del grupo de captura de parens ().
  • \1 el grupo de captura de partido
  • /g partido mundial
  • /p imprimir el resultado

escribí un tool por mí mismo que hace que esto sea más fácil

rip 'abc(\d+)xyz' '$1' 
+2

¡Esta es de lejos la mejor y mejor explicada respuesta hasta el momento! –

+0

Con alguna explicación, es mucho mejor entender qué pasa con nuestro problema. Gracias ! – r4phG

3

Usted puede utilizar awk con match() para acceder al grupo capturado:

$ awk 'match($0, /abc([0-9]+)xyz/, matches) {print matches[1]}' file 
12345 

Esto intenta coincidir con el patrón abc[0-9]+xyz. Si lo hace, almacena sus divisiones en la matriz matches, cuyo primer elemento es el bloque [0-9]+. Como match()devuelve la posición del carácter, o índice, de donde comienza esa subcadena (1, si comienza al principio de la cadena), desencadena la acción print.


Con grep se puede utilizar una mirada atrás y-look-ahead:

$ grep -oP '(?<=abc)[0-9]+(?=xyz)' file 
12345 

$ grep -oP 'abc\K[0-9]+(?=xyz)' file 
12345 

Esto comprueba el patrón [0-9]+ cuando se produce dentro de abc y xyz y sólo imprime los dígitos.

Cuestiones relacionadas