sed: imprimir solo grupo coincidente


133

Quiero tomar los dos últimos números (uno int, uno flotante; seguido de espacios en blanco opcionales) e imprimir solo ellos.

Ejemplo:

foo bar <foo> bla 1 2 3.4

Debería imprimir:

2 3.4

Hasta ahora, tengo lo siguiente:

sed -n  's/\([0-9][0-9]*[\ \t][0-9.]*[\ \t]*$\)/replacement/p' 

me dará

foo bar <foo> bla 1 replacement

Sin embargo, si trato de reemplazarlo con el grupo 1, se imprime toda la línea.

sed -n  's/\([0-9][0-9]*[\ \t][0-9.]*[\ \t]*$\)/\1/p' 

¿Cómo puedo imprimir solo la sección de la línea que coincide con la expresión regular del grupo?

Respuestas:


138

Haga coincidir toda la línea, así que agregue un .*al comienzo de su expresión regular. Esto hace que la línea completa se reemplace con el contenido del grupo

echo "foo bar <foo> bla 1 2 3.4" |
 sed -n  's/.*\([0-9][0-9]*[\ \t][0-9.]*[ \t]*$\)/\1/p'
2 3.4

38
Tuve que agregar la -ropción o `--regexp-extended`, de lo contrario recibí el invalid reference \1 on error 's RHS' del comando.
— Daniel Sokolowski

15
@DanielSokolowski Creo que obtienes ese error si usas (y en )lugar de \(y \).
— Daniel Darabos

3
También recuerde agregar .*al final de la expresión regular si la cadena que desea extraer no siempre está al final de la línea.
— Teemu Leisti

3
Esto no funcionará para mí porque .*es codicioso y sed no tiene un no codicioso.*?
— sondra.kinsey

@DanielDarabos Solo mencione eso (y )no generará errores en ubuntu 16.04. Así que creo que este comentario está desactualizado.
— Li haonan el

72

grep es la herramienta adecuada para extraer.

usando su ejemplo y su expresión regular:

kent$  echo 'foo bar <foo> bla 1 2 3.4'|grep -o '[0-9][0-9]*[\ \t][0-9.]*[\ \t]*$'
2 3.4

12
ideal para todo el grupo, aunque sed es necesario para grupos individuales
— jozxyqk

grep -o no se transfiere a sistemas que ejecutan msysgit pero sed sí.
— cchamberlain

Vea la pregunta vinculada por @jozxyqk para obtener una respuesta que utiliza el mirar hacia adelante y hacia atrás para resolver esto con grep.
— Joachim Breitner

Puede extraer un grupo de un patrón con grep -ollamadas canalizadas . stackoverflow.com/a/58314379/117471
— Bruno Bronosky

12

Y para otra opción, ¡iría con awk!

echo "foo bar <foo> bla 1 2 3.4" | awk '{ print $(NF-1), $NF; }'

Esto dividirá la entrada (estoy usando STDIN aquí, pero su entrada podría ser fácilmente un archivo) en espacios, y luego imprimirá el último campo, pero luego el último campo. Las $NFvariables contienen el número de campos encontrados después de explotar en espacios.

El beneficio de esto es que no importa si lo que precede a los dos últimos campos cambia, siempre y cuando solo desee los dos últimos, seguirá funcionando.


3

El comando de corte está diseñado para esta situación exacta. Se "cortará" en cualquier delimitador y luego puede especificar qué fragmentos se deben generar.

Por ejemplo: echo "foo bar <foo> bla 1 2 3.4" | cut -d " " -f 6-7

Resultará en la salida de: 2 3.4

-d establece el delimitador

-f selecciona el rango de 'campos' para la salida, en este caso, es la sexta a la séptima parte de la cadena original. También puede especificar el rango como una lista, como 6,7.


Para imprimir solo ciertas columnas, awk '{ print $2" "$6 }'
— canalice

@nurettin Creo que tu comentario podría haber sido para una de las respuestas awk.
— carlin.scott

Intenté cortar cuando visité esta página y me di cuenta de sus limitaciones y decidí escribir una versión más generalizada en awk como un comentario para mejorar la calidad de esta publicación.
— nurettin

1
Sí, creo que eso pertenece a una respuesta diferente que involucra awk. El comando de corte para hacer lo que escribió es:cut -d " " -f 2,6
— carlin.scott

ah, no lo sabía, pensé que solo podías dar rangos. Gracias por eso.
— nurettin

2

Estoy de acuerdo con @kent en que esto es muy adecuado grep -o. Si necesita extraer un grupo dentro de un patrón, puede hacerlo con un segundo grep.

# To extract \1 from /xx([0-9]+)yy/
$ echo "aa678bb xx123yy xx4yy aa42 aa9bb" | grep -Eo 'xx[0-9]+yy' | grep -Eo '[0-9]+'
123
4

# To extract \1 from /a([0-9]+)b/
$ echo "aa678bb xx123yy xx4yy aa42 aa9bb" | grep -Eo 'a[0-9]+b' | grep -Eo '[0-9]+'
678
9
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.