¿Cómo generar solo grupos capturados con sed?

¿Hay alguna manera de decirle a sed que solo muestre grupos capturados? Por ejemplo, dada la entrada:

 This is a sample 123 text and some 987 numbers 

y patrón:

 /([\d]+)/ 

¿Puedo obtener solo la salida 123 y 987 en la forma formateada por referencias?

La clave para hacer que esto funcione es decirle a sed que excluya lo que no quiere que se genere, y que especifique lo que quiere.

 string='This is a sample 123 text and some 987 numbers' echo "$string" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p' 

Esto dice:

  • no imprime por defecto cada línea ( -n )
  • excluir cero o más sin dígitos
  • incluir uno o más dígitos
  • excluir uno o más no dígitos
  • incluir uno o más dígitos
  • excluir cero o más sin dígitos
  • imprima la sustitución ( p )

En general, en sed puede capturar grupos usando paréntesis y generar lo que captura utilizando una referencia posterior:

 echo "foobarbaz" | sed 's/^foo\(.*\)baz$/\1/' 

saldrá “bar”. Si usa -r ( -E para OS X) para expresiones regulares extendidas, no necesita escaparse de los paréntesis:

 echo "foobarbaz" | sed -r 's/^foo(.*)baz$/\1/' 

Puede haber hasta 9 grupos de captura y sus referencias. Las referencias anteriores están numeradas en el orden en que aparecen los grupos, pero se pueden usar en cualquier orden y se pueden repetir:

 echo "foobarbaz" | sed -r 's/^foo(.*)b(.)z$/\2 \1 \2/' 

saca “a bar a”.

Si tiene GNU grep (también puede funcionar en BSD, incluido OS X):

 echo "$string" | grep -Po '\d+' 

o variaciones tales como:

 echo "$string" | grep -Po '(?< =\D )(\d+)' 

La opción -P habilita expresiones regulares compatibles con Perl. Ver man 3 pcrepattern o man 3 pcresyntax .

Sed tiene hasta nueve patrones recordados, pero necesita usar paréntesis escapados para recordar partes de la expresión regular.

Vea aquí para ejemplos y más detalles

puedes usar grep

 grep -Eow "[0-9]+" file 

Creo que el patrón dado en la pregunta era solo a modo de ejemplo, y el objective era hacer coincidir cualquier patrón.

Si tiene un sed con la extensión GNU que permite la inserción de una nueva línea en el espacio del patrón, una sugerencia es:

 > set string = "This is a sample 123 text and some 987 numbers" > > set pattern = "[0-9][0-9]*" > echo $string | sed "s/$pattern/\n&\n/g" | sed -n "/$pattern/p" 123 987 > set pattern = "[az][az]*" > echo $string | sed "s/$pattern/\n&\n/g" | sed -n "/$pattern/p" his is a sample text and some numbers 

Estos ejemplos son con tcsh (sí, sé que es el shell incorrecto) con CYGWIN. (Editar: para bash, eliminar conjunto y los espacios alrededor de =.)

Tratar

 sed -n -e "/[0-9]/s/^[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\).*$/\1 \2 \3 \4 \5 \6 \7 \8 \9/p" 

Tengo esto bajo cygwin:

 $ (echo "asdf"; \ echo "1234"; \ echo "asdf1234adsf1234asdf"; \ echo "1m2m3m4m5m6m7m8m9m0m1m2m3m4m5m6m7m8m9") | \ sed -n -e "/[0-9]/s/^[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\).*$/\1 \2 \3 \4 \5 \6 \7 \8 \9/p" 1234 1234 1234 1 2 3 4 5 6 7 8 9 $ 

corrida (s) de dígitos

Esta respuesta funciona con cualquier recuento de grupos de dígitos. Ejemplo:

 $ echo 'Num123that456are7899900contained0018166intext' | > sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp' 123 456 7899900 0018166 

Respuesta expandida

¿Hay alguna manera de decirle a sed que solo muestre grupos capturados?

Sí. reemplace todo el texto por el grupo de captura:

 $ echo 'Number 123 inside text' | sed 's/[^0-9]*\([0-9]\{1,\}\)[^0-9]*/\1/' 123 s/[^0-9]* # several non-digits \([0-9]\{1,\}\) # followed by one or more digits [^0-9]* # and followed by more non-digits. /\1/ # gets replaced only by the digits. 

O con syntax extendida (menos comillas inversas y permite el uso de +):

 $ echo 'Number 123 in text' | sed -E 's/[^0-9]*([0-9]+)[^0-9]*/\1/' 123 

Para evitar imprimir el texto original cuando no hay número, use:

 $ echo 'Number xxx in text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1/p' 
  • (-n) No imprima la entrada por defecto.
  • (/ p) imprima solo si se hizo un reemplazo.

Y para unir varios números (y también imprimirlos):

 $ echo 'N 123 in 456 text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1 /gp' 123 456 

Eso funciona para cualquier conteo de corridas de dígitos:

 $ str='Test Num(s) 123 456 7899900 contained as0018166df in text' $ echo "$str" | sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp' 123 456 7899900 0018166 

Que es muy similar al comando grep:

 $ str='Test Num(s) 123 456 7899900 contained as0018166df in text' $ echo "$str" | grep -Po '\d+' 123 456 7899900 0018166 

Acerca de \ d

y patrón: /([\d]+)/

Sed no reconoce la syntax ‘\ d’ (atajo). El equivalente ascii usado anteriormente [0-9] no es exactamente equivalente. La única solución alternativa es usar una clase de caracteres: ‘[[: digit:]] `.

La respuesta seleccionada usa tales “clases de caracteres” para construir una solución:

 $ str='This is a sample 123 text and some 987 numbers' $ echo "$str" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p' 

Esa solución solo funciona para (exactamente) dos carreras de dígitos.

Por supuesto, como la respuesta se está ejecutando dentro del shell, podemos definir un par de variables para hacer que dicha respuesta sea más corta:

 $ str='This is a sample 123 text and some 987 numbers' $ d=[[:digit:]] D=[^[:digit:]] $ echo "$str" | sed -rn "s/$D*($d+)$D+($d+)$D*/\1 \2/p" 

Pero, como ya se explicó, usar un comando s/…/…/gp es mejor:

 $ str='This is 75577 a sam33ple 123 text and some 987 numbers' $ d=[[:digit:]] D=[^[:digit:]] $ echo "$str" | sed -rn "s/$D*($d+)$D*/\1 /gp" 75577 33 123 987 

Eso cubrirá tanto las ejecuciones repetidas de dígitos como la escritura de un comando corto (er).

Renunciar y usar Perl

Como sed no lo corta, simplemente tiramos la toalla y usamos Perl, al menos es LSB mientras grep extensiones de GNU no son 🙂

  • Imprima toda la parte coincidente, no se necesitan grupos coincidentes o se necesita un aspecto subyacente:

     cat <  

    Salida:

     12 3456 
  • Único partido por línea, a menudo campos de datos estructurados:

     cat <  

    Salida:

     1 34 

    Con lookbehind:

     cat <  
  • Múltiples campos:

     cat <  

    Salida:

     1 2 34 56 
  • Varias coincidencias por línea, a menudo datos no estructurados:

     cat <  

    Salida:

     1 34 78 

    Con lookbehind:

     cat EOS< < | perl -lane 'print m/(?<=a)(\d+)/g' a1 b2 a34 b56 a78 b90 EOS 

    Salida:

     1 3478 

No es lo que el OP solicitó (capturar grupos) pero puede extraer los números usando:

 S='This is a sample 123 text and some 987 numbers' echo "$S" | sed 's/ /\n/g' | sed -r '/([0-9]+)/ !d' 

Da lo siguiente:

 123 987