¿Cómo puedo sustituir varias instancias de un personaje con la misma cantidad de instancias de un personaje diferente en linux sed?

2

Necesito sustituir un conjunto repetitivo de caracteres (2 o más) con el número exacto de caracteres de reemplazo. Necesito hacer esto con sed o dentro de vi.

Ejemplos

"abc,,,def" becomes "abc|||dev"
"1245d,,,,,22" becomes "1245d|||||22"

Gracias

hombre X
fuente
¿Entonces necesita un reemplazo global de un personaje con otro, en un archivo?
Alex
Sí, pero solo donde hay más de 2 repeticiones.
xman
Esto se puede lograr fácilmente con perl: perl -lape 's/,{2,}/"|" x length($&)/ge'. Lástima que no puedas usarlo (¿por qué?). Se puede llamar desde adentro vi, no sé si eso es admisible en su situación.
simlev

Respuestas:

1

Tubería a través sed, como

echo "abc,,,def" | sed 's/,/|/g'

pero recomendaría usar

tr ',' '|'

en este caso.

jvb
fuente
Esto no funcionaria. Como se menciona en la pregunta, debe coincidir con 2 o más repeticiones del personaje.
xman
Gracias por aclarar, mi culpa. Por lo tanto, desea reemplazar solo las partes entre paréntesis: echo "a, bc ,,, def" | sed -E 's / (,, +) / (\ 1) / g' -> a, bc (,,,) def - investigando ahora ...
jvb
"solución" parcial: echo "a,b,,c,,,c" | sed 's/,,,,/||||/g; s/,,,/|||/g; s/,,/||/g' (expanda el patrón si es necesario). No es realmente una buena solución, pero podría ser útil si se conoce el número máximo de repeticiones.
jvb
Es cierto ... su enfoque funcionaría si conocemos el número máximo de repeticiones. Sin embargo, sería bueno si hay una solución genérica.
xman
¿Qué hay de echo "a,b,,c,,,c" | sed -E 's/([^,]),([^,])/\1#\2/g; s/,/|/g; s/#/,/g'? Esto convertirá cada sola "" a '#', entonces todo lo restante (= múltiple) "" a '|', y entonces todo salvo '#' a "". Pero necesita un carácter que no se utiliza en la secuencia de entrada (# aquí).
jvb
1

Disculpe por no comentar, no tengo 50 reputación.

Esta solución fallará si hay más patrones, como abc,,,def,g.

sed -n 's/[^,],,/&/;tsubs;p;d;:subs s/,/|/g;p' <<<'abc,,,def
abc,,def
abc,,,def,g
abc,def'
Paulo
fuente
0

Esto es prácticamente imposible, debido a la forma en que funcionan las expresiones regulares. Como jvb ya señaló, una solución es simple (aunque no necesariamente corta) si se conoce el número máximo de caracteres de origen consecutivos. Si no, es posible cambiar todos los caracteres de origen primero, y luego volver a cambiar los caracteres individuales en un segundo paso. Sin embargo, esto solo funciona si el carácter de destino no aparece en la secuencia de entrada, o si puede usar un carácter que se sabe que no aparece en la secuencia de entrada como un objetivo intermedio.

Además, debe tener en cuenta los casos de esquina de un solo carácter de origen que aparece al principio o al final de la línea. Así:

tr ',' '|' < file | sed 's/\([^|]\)|\([^|]\)/\1,\2/;s/^|\([^|]\)/,\1/;s/\([^|]\)|$/\1,/'

o

sed 's/,/|/g;s/\([^|]\)|\([^|]\)/\1,\2/;s/^|\([^|]\)/,\1/;s/\([^|]\)|$/\1,/' file

Una solución que use un lenguaje que tenga una noción de longitud de cadena sería más robusta.

Michael Vehrs
fuente