Respuestas:
La clave para que esto funcione es indicar sed
que excluya lo que no desea que se genere, 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'
Esto dice:
-n
)p
)En general, en los sed
grupos de captura con paréntesis y salida de lo que captura con una referencia inversa:
echo "foobarbaz" | sed 's/^foo\(.*\)baz$/\1/'
dará salida a "bar". Si usa -r
( -E
para OS X) para expresiones regulares extendidas, no necesita escapar de los paréntesis:
echo "foobarbaz" | sed -r 's/^foo(.*)baz$/\1/'
Puede haber hasta 9 grupos de captura y sus referencias posteriores. Las referencias posteriores están numeradas en el orden en que aparecen los grupos, pero pueden usarse en cualquier orden y pueden repetirse:
echo "foobarbaz" | sed -r 's/^foo(.*)b(.)z$/\2 \1 \2/'
salidas "una barra a".
Si tiene GNU grep
(también puede funcionar en BSD, incluido OS X):
echo "$string" | grep -Po '\d+'
o variaciones como:
echo "$string" | grep -Po '(?<=\D )(\d+)'
La -P
opción habilita las expresiones regulares compatibles con Perl. Ver man 3 pcrepattern
o man
3 pcresyntax
.
sed
ejemplo, si usas la -r
opción (o -E
para OS X, IIRC) no necesitas escapar de los paréntesis. La diferencia es que entre las expresiones regulares básicas y las expresiones regulares extendidas ( -r
).
Sed tiene hasta nueve patrones recordados, pero debe usar paréntesis escapados para recordar partes de la expresión regular.
Vea aquí para ejemplos y más detalles
sed -e 's/version=\(.+\)/\1/' input.txt
Esto le sigue la salida del conjunto entrada.txt
\+
lugar de +
. Y no entiendo por qué la gente usa -e
solo un comando sed.
sed -e -n 's/version=\(.+\)/\1/p' input.txt
see: mikeplate.com/2012/05/09/…
sed -E
para usar las llamadas expresiones regulares "modernas" o "extendidas" que se parecen mucho más a Perl / Java / JavaScript / Go / cualquier tipo de sabor. (Compare con grep -E
o egrep
.) La sintaxis predeterminada tiene esas extrañas reglas de escape y se considera "obsoleta". Para obtener más información sobre las diferencias entre los dos, ejecute man 7 re_format
.
puedes usar grep
grep -Eow "[0-9]+" file
o
opción está ahí: unixhelp.ed.ac.uk/CGI/man-cgi?grep : -o, --only-matching Muestra solo la parte de una línea coincidente que coincide con PATTERN
grep -Eow -e "[0-9]+" -e "[abc]{2,3}"
no sé cómo podría requerir que esas dos expresiones estén en una línea aparte de las tuberías de un grep anterior (que aún no podría funcionar si cualquiera de los patrones coincide más de una vez en una línea )
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
¿Hay alguna forma de decirle a sed que solo genere grupos capturados?
Si. 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 sintaxis 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 un número, use:
$ echo 'Number xxx in text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1/p'
Y para hacer coincidir 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 recuento de ejecuciones 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
Lo cual 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
y patrón:
/([\d]+)/
Sed no reconoce la sintaxis '\ d' (acceso directo). El equivalente ascii utilizado anteriormente [0-9]
no es exactamente equivalente. La única solución alternativa es usar una clase de caracteres: '[[: dígito:]] `.
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 corridas de dígitos.
Por supuesto, a medida que la respuesta se ejecuta dentro del shell, podemos definir un par de variables para acortar dicha respuesta:
$ 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ó, utilizar un s/…/…/gp
comando 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 repeticiones de dígitos como la escritura de un comando corto (er).
Creo que el patrón dado en la pregunta fue solo a modo de ejemplo, y el objetivo 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 = "[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í, sé que es el shell incorrecto) con CYGWIN. (Editar: Para bash, elimine el conjunto y los espacios alrededor =.)
+
, necesitarías escapar de él o usar la -r
opción ( -E
para OS X). También puede usar \{1,\}
( -r
o -E
sin el escape).
Renunciar y usar Perl
Como sed
no lo corta, simplemente tiremos la toalla y usemos Perl, al menos es LSB, mientras que las grep
extensiones GNU no son :-)
Imprima toda la parte coincidente, no se necesitan grupos coincidentes ni mirar hacia atrás:
cat <<EOS | perl -lane 'print m/\d+/g'
a1 b2
a34 b56
EOS
Salida:
12
3456
Coincidencia única 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 mirar atrás:
cat <<EOS | perl -lane 'print m/(?<=a)(\d+)/'
a1 b2
a34 b56
EOS
Múltiples campos:
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
Múltiples coincidencias por línea, a menudo datos no estructurados:
cat <<EOS | perl -lape 's/.*?a(\d+)|.*/$1 /g'
a1 b2
a34 b56 a78 b90
EOS
Salida:
1
34 78
Con mirar atrás:
cat EOS<< | perl -lane 'print m/(?<=a)(\d+)/g'
a1 b2
a34 b56 a78 b90
EOS
Salida:
1
3478
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
$
No es lo que solicitó el OP (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
sed
activar expresiones regulares extendidas con la-E
bandera.