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?

mort
fuente

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
iruvar
fuente
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
Kent
fuente
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.

chooban
fuente
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.

carlin.scott
fuente
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
Bruno Bronosky
fuente