los grupos de captura de sed no funcionan

27

Tengo una cadena del formato [0-9]+\.[0-9]+\.[0-9]. Necesito extraer el primer, segundo y tercer número por separado. Según tengo entendido, los grupos de captura deberían ser capaces de esto. Debería poder usar sed "s/\([0-9]*\)/\1/gpara obtener el primer número, sed "s/\([0-9]*\)/\2/gobtener el segundo número y sed "s/\([0-9]*\)/\3/gobtener el tercer número. En cada caso, sin embargo, obtengo toda la cadena. ¿Por qué está pasando esto?

Melab
fuente
66
Los grupos de captura capturan todo el grupo ... no elementos individuales en el grupo. Necesitas algo como 's/\([0-9]\)\([0-9]\)\([0-9]\).*/\1\2\3/'capturar números individuales.
Munir

Respuestas:

45

No podemos darle una respuesta completa sin un ejemplo de su aporte, pero puedo decirle que su comprensión de los grupos de captura es incorrecta. No los usa secuencialmente, solo se refieren a la expresión regular en el lado izquierdo del mismo operador de sustitución. Si captura, por ejemplo, /(foo)(bar)(baz)/y luego fooserá \1, barserá \2y bazserá \3. No puede hacerlo s/(foo)/\1/; s/(bar)/\2/porque, en la segunda s///llamada, solo hay un grupo capturado, por \2lo que no se definirá.

Entonces, para capturar sus tres grupos de dígitos, necesitaría hacer:

sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1 : \2 : \3/'

O, lo más legible:

sed -E 's/([0-9]*)\.([0-9]*)\.([0-9]*)/\1 : \2 : \3/'
terdon
fuente
1
¿Cuál es el beneficio de escapar del paréntesis en el primer ejemplo?
Josh M.
2
@JoshM. necesita escapar de ellos para que puedan usarse para capturar patrones. Normalmente /(foo)/en sed coincidirá con un (carácter literal , seguido de fooy luego un literal ). Si desea capturar un grupo, debe escapar de los paréntesis o usar la -Eopción.
terdon
Casi siempre uso la -rbandera, así que supongo que es por eso que aún no me he encontrado con esto.
Josh M.
1
@JoshM. Sí, la -rbandera también lo hará, pero no es portátil. GNU sed lo admite, pero muchos otros no. El -Ees más universal.
terdon
9

Ejemplo:

$ echo "123.456.78" |sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'
123

$ echo "123.456.78" |sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'
456

$ echo "123.456.78" |sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'
78

O, todos juntos:

$ echo "123.456.78" |sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1 : \2 : \3/'
123 : 456 : 78
jai_s
fuente
2

Use Sed con -r, --regexp-extended para evitar todos los paréntesis escapados.

echo "1234.567.89" | sed -r 's/([0-9]+)\.([0-9]+)\.([0-9]+)/\1, \2, \3/' 
1234, 567, 89    #output
Surya
fuente