¿Cómo reemplazar recursivamente caracteres con sed?

13

¿Es posible reemplazar las ocurrencias de una secuencia de caracteres de forma recursiva sin repetir nuevamente la misma secuencia?

Al realizar un sedcomo en los siguientes escenarios, puedo obtener el resultado mencionado.

$ echo XX | sed -e 's/XX/XoX/g'
XoX  
$ echo XXX | sed -e 's/XX/XoX/g'
XoXX  
$ echo XXXX | sed -e 's/XX/XoX/g'
XoXXoX  

Sin embargo, espero que la salida siga el siguiente comportamiento.

Entrada:

XX
XXX
XXXX

Rendimiento esperado:

XoX
XoXoX
XoXoXoX

¿Es posible lograr el comportamiento esperado con sed solo?

Ishan Madhusanka
fuente

Respuestas:

24

Tu puedes hacer:

> echo XXXX | sed -e ':loop' -e 's/XX/XoX/g' -e 't loop'
XoXoXoX

Con:

  • -e ':loop' : Crear una etiqueta de "bucle"
  • -e 't loop' : Salta a la etiqueta de "bucle" si la sustitución anterior fue exitosa
Gohu
fuente
10

En este caso particular, sería útil mirar hacia adelante o hacia atrás. Creo que GNU sedno es compatible con estos. Con perl:

perl -ne 's/X(?=X)/Xo/g; print;'

También puede usar lookbehind y lookahead como:

s/(?<=X)(?=X)/o/g

Dónde:

(?<=X)es una mirada positiva hacia atrás, una afirmación de longitud cero que asegura que tengamos una X antes de la posición actual
(?=X)es una afirmación positiva hacia adelante, una afirmación de longitud cero que asegura que tengamos una X después de la posición actual

Utilizando en un perl one-liner:

perl -pe 's/(?<=X)(?=X)/o/g' inputfile

Dónde:

-p hace que Perl asuma un bucle alrededor del programa con una impresión implícita de la línea actual

Toto
fuente
5

La respuesta en bucle es la forma general de hacer lo que está pidiendo.

Sin embargo, en el caso de sus datos, suponiendo que esté utilizando GNU, simplemente puede hacer:

sed 's/\B/o/g'

Las opciones \by \Bson extensiones regex :

  • \b coincide con los límites de las palabras, es decir, la transición de un carácter de "palabra" a un carácter de "no palabra", o viceversa
  • \Bcoincide con el opuesto de \b. es decir, las lagunas "dentro" de las palabras. Esto nos permite insertar caracteres dentro de una palabra pero no fuera, según sea necesario.

Pruébalo en línea .

Esto supone que los caracteres de entrada son de hecho todos caracteres de "palabra".


Alternativamente, si no tiene GNU sed, o si los caracteres de entrada no son todos caracteres de "palabra", aún puede lograr su objetivo sin bucle:

sed 's/./&o/g;s/o$//'

Esto simplemente coloca un odespués de cada carácter y luego elimina el final ode la cadena.

Pruébalo en línea .

Trauma digital
fuente
1
Esto supone que las cadenas de entrada consisten en un número Xy nada más. Ambas soluciones fallan si hay otros personajes presentes ...
Año
@AnoE En la segunda muestra, eso se arregla con un simple reemplazo de Xby .. Por favor vea editar.
Trauma digital
No es equivalente al caso que dio el OP. Dio las RE exactas que necesita (cambiar las ocurrencias de XX en una cadena). Sus versiones solo dan el mismo resultado que el suyo para las mismas cadenas de entrada que él dio; no para cadenas de entrada genéricas.
AnoE
4

Verifiqué si hay algún tipo de bandera para que esto suceda.
Incluso si ese comportamiento estuviera allí, consumirá muchos recursos.

Sin embargo, en este caso de uso particular, es posible tener la expresión solo dos veces y lograr la funcionalidad requerida. es decir, con 2 sedexpresiones repetitivas .

echo XX | sed -e 's/XX/XoX/g' -e 's/XX/XoX/g'     # outputs XoX
echo XXX | sed -e 's/XX/XoX/g' -e 's/XX/XoX/g'    # outputs XoXoX
echo XXXX | sed -e 's/XX/XoX/g' -e 's/XX/XoX/g'   # outputs XoXoXoX
Ishan Madhusanka
fuente