tutorial español regex string bash sed regex-group

regex - español - django tutorial



¿Por qué sed no imprime un grupo opcional? (2)

Tengo dos cadenas, digamos foo_bar y foo_abc_bar . Me gustaría hacer coincidir ambos, y si el primero coincide, me gustaría enfatizarlo con = sign. Entonces, mi suposición fue:

echo ''foo_abc_bar'' | sed -r ''s/(foo).*(abc)?.*(bar)//1=/2=/3/g'' > foo==bar

o

echo ''foo_abc_bar'' | sed -r ''s/(foo).*((abc)?).*(bar)//1=/2=/3/g'' > foo==

Pero como resultado anterior muestra que ninguno de ellos funciona.

¿Cómo puedo especificar un grupo opcional que coincidirá si la cadena lo contiene o simplemente omitir si no?


Tal vez podrías simplemente usar:

echo ''foo_abc_bar'' | sed -r ''s/(foo|bar|abc)_?//1=/g'' echo ''foo_bar'' | sed -r ''s/(foo|bar|abc)_?//1=/g'' > foo=abc=bar= > foo=bar=

Esto evita la foo==bar que obtienes con foo_bar y me pareció un poco raro mostrar énfasis poniendo = veces antes del partido, a veces después del partido.


La solución:

echo ''foo_abc_bar'' | sed -r ''s/(foo)_((abc)_)?(bar)//1=/3=/4/g''

Por qué tus intentos anteriores no funcionaron:

.* es codicioso, por lo que para la expresión regular (foo).*(abc)?.*(bar) intentando hacer coincidir ''foo_abc_bar'' la (foo) coincidirá con ''foo'' , y luego la .* coincidirá inicialmente con el resto de la cadena ( ''_abc_bar'' ). La expresión regular continuará hasta que llegue al grupo requerido (bar) y esto fallará, momento en el que la expresión regular retrocederá al renunciar a los caracteres que hayan coincidido con .* . Esto sucederá hasta que el primero .* Solo coincida con ''_abc_'' , en cuyo punto el grupo final puede coincidir con ''bar'' . Entonces, en lugar de que el ''abc'' en su cadena coincida en el grupo de captura, se empareja en el no capturado .* .

Explicación de mi solución:

Lo primero y más importante es reemplazar el .* Con _ , no hay necesidad de hacer coincidir cualquier cadena arbitraria si sabes cuál será el separador. Lo siguiente que debemos hacer es averiguar exactamente qué parte de la cadena es opcional. Si las cadenas ''foo_abc_bar'' y ''foo_bar'' son ambas válidas, entonces ''abc_'' en el medio es opcional. Podemos poner esto en un grupo opcional usando (abc_)? . El último paso es asegurarnos de que todavía tenemos la cadena ''abc'' en un grupo de captura, lo que podemos hacer envolviendo esa porción en un grupo adicional, ¿así que terminamos con ((abc)_)? . Necesitamos ajustar el reemplazo porque hay un grupo adicional, entonces en lugar de /1=/2=/3 usamos /1=/3=/4 , /2 sería la cadena ''abc_'' (si coincide) . Tenga en cuenta que en la mayoría de las implementaciones de expresiones regulares también podría haber utilizado un grupo que no captura y continuó usando /1=/2=/3 , pero sed no admite grupos que no capturan.

Una alternativa:

Creo que la expresión regular anterior es tu mejor opción porque es la más explícita (solo coincidirá con las cadenas exactas en las que estás interesado). Sin embargo, también podría evitar el problema descrito anteriormente mediante el uso de la repetición diferida (coincide con el menor número de caracteres posible) en lugar de la repetición codiciosa (coincide con tantos caracteres como sea posible). Puede hacer esto cambiando el .* A. .*? , entonces tu expresión sería algo como esto:

echo ''foo_abc_bar'' | sed -r ''s/(foo).*?(abc).*?(bar)//1=/2=/3/g''