2010-05-06 23 views
175

¿Hay alguna forma de decirle al sed que muestre solo grupos capturados? Por ejemplo, dada la entrada:¿Cómo se generan solo los grupos capturados con sed?

This is a sample 123 text and some 987 numbers 

y el patrón:

/([\d]+)/ 

¿Puedo obtener solamente 123 y 987 de salida en la forma formateada por referencias anteriores?

Respuesta

192

El t clave o hacer que esto funcione es indicar sed para excluir lo que no desea que se muestre, así como especificar lo que desea.

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

Esta dice:

  • no por defecto a la impresión de cada línea (-n)
  • excluye cero o más dígitos no
  • incluyen uno o más dígitos
  • excluir uno o más no dígitos
  • incluyen uno o más dígitos
  • excluir a cero o más no-dígitos
  • impresión de la sustitución (p)

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

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

o variaciones tales como:

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

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

+17

Como nota, OSX Mountain Lion ya no es compatible con PCRE en grep. – yincrash

+1

Como nota al margen, la opción grep -o no es compatible con Solaris 9. Además, Solaris 9 no es compatible con la opción sed -r. :( – BlackSheep

+6

Pídale a su administrador de sistemas que instale gsed. Se sorprenderá de lo que le darán algunas donas ... – avgvstvs

44

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

Ver here de ejemplos y más detalles

+48

'sed-e 's/version = \ (. + \)/\ 1 /' input.txt' esto aun así generará la entrada completa.txt – Pablo

+3

El comentario de @Pablo debe ser la respuesta aceptada – Hilikus

+0

@Pablo , En su patrón, debe escribir '\ +' en lugar de '+'. Y no entiendo por qué las personas usan '-e' solo para un comando sed. –

5

Try

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 
$ 
24

puede utilizar grep

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

@ ghostdog74: Estoy totalmente de acuerdo contigo. ¿Cómo puedo hacer que greo solo muestre grupos capturados? – Pablo

+1

@Michael - por eso está la opción 'o' - http://unixhelp.ed.ac.uk/CGI/man-cgi?grep: -o, --only-matching Muestra solo la parte de una línea coincidente que coincida con el PATRÓN –

+9

@Bert F: entiendo la parte correspondiente, pero no es un grupo de captura. Lo que quiero es tener esto ([0-9] +). + ([Abc] {2,3}) entonces hay 2 grupos de captura. Quiero emitir SÓLO grupos de captura por referencias o de alguna otra manera. – Pablo

6

creo que el modelo dado en la pregunta era sólo a modo de ejemplo, y que el objetivo era igualar cualquier patrón.

Si usted tiene un sed con la extensión de GNU que permite la inserción de una nueva línea en el espacio de patrones, 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 = "[a-z][a-z]*" 
> 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í, lo es el caparazón equivocado) con CYGWIN. (Editar: para bash, remove set, y los espacios alrededor de =.)

+0

@Joseph: gracias, en base a mi tarea, siento que grep es más natural, como ghostdog74 sugirió. necesita averiguar cómo hacer que grep genere solo los grupos de captura, no la coincidencia completa. – Pablo

+1

Solo una nota, pero el signo más n '+' significa 'uno o más' que eliminaría la necesidad de repetirse en los patrones. Entonces, "[0-9] [0-9] *" se convertiría en "[0-9] +" – RandomInsano

+3

@RandomInsano: para usar el '+', necesitaría escaparse o usar el '-r 'opción (' -E' para OS X). También puede usar '\ {1, \}' (o '-r' o' -E' sin el escape). –

1

No es lo que el PO pidió (grupos de captura), pero se 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' 

dicta la siguiente:

123 
987 
4

renunciar y usar Perl

Dado que sed no se corta, simplemente tiramos la toalla y usamos Perl, al menos es LSB mientras grep extensiones GNU no son :-)

  • Imprimir toda la parte coincidente, no se encontraron grupos de búsqueda hacia atrás o necesarios:

    cat <<EOS | perl -lane 'print m/\d+/g' 
    a1 b2 
    a34 b56 
    EOS 
    

    Salida:

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

    cat <<EOS | perl -lape 's/.*?a(\d+).*/$1/g' 
    a1 b2 
    a34 b56 
    EOS 
    

    Salida:

    1 
    34 
    

    Con búsqueda hacia atrás: campos

    cat <<EOS | perl -lane 'print m/(?<=a)(\d+)/' 
    a1 b2 
    a34 b56 
    EOS 
    
  • múltiples:

    cat <<EOS | perl -lape 's/.*?a(\d+).*?b(\d+).*/$1 $2/g' 
    a1 c0 b2 c0 
    a34 c0 b56 c0 
    EOS 
    

    Salida:

    1 2 
    34 56 
    
  • partidos múltiples por línea, los datos no estructurados a menudo:

    cat <<EOS | perl -lape 's/.*?a(\d+)|.*/$1 /g' 
    a1 b2 
    a34 b56 a78 b90 
    EOS 
    

    Salida:

    1 
    34 78 
    

    Con búsqueda hacia atrás:

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

    Salida:

    1 
    3478 
    
3

ejecución (es) de dígitos

Esta respuesta funciona con cualquier cantidad 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 muestre solo grupos capturados?

Sí.reemplazar 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 la sintaxis extendida (menos acentos graves y permitir el uso de +):

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

Para evitar que se imprima el texto original cuando no hay un número, el uso :

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

Y para que coincida con 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 

que funciona para cualquier recuento de carreras 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 \ d

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

Sed no reconoce el (acceso directo) sintaxis '\ d'. El equivalente ascii usado anteriormente [0-9] no es exactamente equivalente. La única solución alternativa es usar una clase de caracteres: '[[: digit:]] `.

La utilización respuesta seleccionada 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 sólo funciona para (exactamente) dos series de dígitos.

Por supuesto, como la respuesta se está ejecutando dentro de la cáscara, podemos definir un par de variables para realizar dicha respuesta más corto:

$ 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 ha explicado, mediante 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 ejecuciones repetidas de dígitos como la escritura de un comando corto (er).

Cuestiones relacionadas