¿Cómo puedo usar sed o ex para reemplazar un bloque (código de varias líneas) con un nuevo bloque de texto (código)?


9

Tengo que reemplazar un bloque grande de texto (código de script de shell) en un archivo con otro bloque de texto.

Estoy impresionado con el ¿Cómo puedo usar sed para reemplazar una cadena de varias líneas? respondido por antak y Multi-line replace respondido por Bruce Ediger

Pero tengo algunos problemas para usarlos.

  1. antak ya mencionó en su respuesta que transmitir archivos completos ( 1h;2,$H;$!d;g;) al búfer no es recomendable para archivos grandes, porque sobrecarga la memoria.

  2. Sé que sedse puede usar con la función de bloque para preservar el texto fuera del bloque sin cambios. Quiero usar esta función Pero si lo uso,

    sed -i '/marker1/,/marker2/s/.*/new text (code)/' filename
    

insertará texto nuevo (código) repetidamente para cada transmisión. Por lo tanto, tengo que hacer el bloque visual como una secuencia, usando algo similar a lo sugerido por antak anteriormente, pero para el bloque (no para todo el archivo).

  1. Como mencionó Bruce Ediger, se puede probar la función de agregar exque comienza con el afinal con .(punto), pero mi nuevo texto (código) contiene líneas que comienzan con un punto, que puede considerarse como el punto de sintaxis de adición. ¿Cómo puedo usarlo en esta situación?

  2. ex's dd' número de líneas 'puede eliminar varias líneas, pero si tengo un bloque entre / marker1 / y / marker2 / con el número de líneas no fijo (variable) se debe reemplazar con un nuevo texto (código), cómo hacerlo eso ?


Su uso del término "bloqueo visual" me parece inusual y confuso. ¿Quiere decir simplemente un bloque (grupo) de líneas completas consecutivas?
Scott

Sí, estoy editando la pregunta para evitar confusiones.
gibies

¿Cuál es la fuente del texto de reemplazo? ¿Está en un archivo o es "solo en la mente de los programadores"?
don_crissti

Esto es para parchear la situación de un script de shell. De ahí la fuente para el texto de reemplazo es la mente del programador (o un script maestra en la que sedo excomando funciona).
gibies

Don Crissti Hubiera usado rel comando leer archivo para copiar el texto de otro archivo, si la fuente del texto de reemplazo está en un archivo.
gibies

Respuestas:


10

Sugiero usar el comando c hange (que es esencialmente un d elete junto con un a ppend, aunque el apéndice solo se aplica a la última línea en el rango que es exactamente lo que desea aquí):

sed -i '/marker1/,/marker2/c\
New text 1\
New text 2' filename

Aquí usando sedla sintaxis de GNU para la edición in situ ( -i). Ese ccomando es, por lo demás, estándar y portátil. GNU sedsoporta:

sed '/marker1/,/marker2/cNew text 1\
New text 2' filename

como una extensión no estándar.

Los caracteres de nueva línea y barra invertida deben escapar (con barra invertida) en el texto de reemplazo.


El comando de cambio funciona bien para ambos sedy expor igual. Muchas gracias.
gibies

¿Hay alguna forma de evitar "\" y usar este documento sedcomo en el caso de la exsintaxis sugerida por Bruce Ediger en el reemplazo de varias líneas .
gibies

No necesitaría las barras diagonales inversas si utiliza el rcomando discutido en otras respuestas a esa pregunta. No estoy seguro de cómo usarías un documento aquí sed.
G-Man dice 'Reincorporar a Monica' el

8

Usando GNU sed

Para reemplazar líneas que comienzan una coincidencia de línea 3y continúan a una línea que coincide 5con New Code:

$ seq 8 | sed '/3/,/5/{/5/ s/.*/New Code/; t; d}'
1
2
New Code
6
7
8

Para las líneas en el rango /3/,/5/, probamos para ver si la línea coincide 5(lo que significa que esta es la última línea del grupo) y, de ser así, hacemos la sustitución para insertar New Code. Si se realizó la sustitución, el tcomando le dice a sed que salte al final de los comandos (en cuyo caso New Codese imprime). Si no, el dcomando le dice a sed que elimine la línea.

Todas las demás líneas se imprimen normalmente.

Para cambiar el archivo en su lugar, -ise puede usar la opción:

sed -i.bak '/3/,/5/{/5/ s/.*/New Code/; t; d}' file

Usando awk

Para hacer lo mismo usando awk:

$ seq 8 | awk '/3/,/5/{if (!f)print "New Code"; f=1; next}; 1'
1
2
New Code
6
7
8

El comando awk trata especialmente las líneas del rango /3/,/5/. Para líneas en ese rango, probamos para ver si fes cero (es decir, si !fes verdadero) y, si es así, imprimimos New Codey establecemos fen 1 y luego omitimos el resto de los comandos y saltamos a la nextlínea.

Para líneas fuera del rango /3/,/5/, no se realiza ningún salto y esto 1hace que se imprima la línea. En más detalle, 1es una condición. Como 1no es cero, la condición se evalúa como verdadera. Como no se asocia ninguna acción con la condición, se realiza la acción predeterminada, que es imprimir la línea. Por 1lo tanto, es una forma abreviada de imprimir la línea.

Para cambiar un archivo en su lugar, la -i inplaceopción se puede usar con GNU awk4.1 o superior:

awk -i inplace '/3/,/5/{if (!f)print "New Code"; f=1; next} 1' file

frio. No soy bueno awk, ¿podría explicarme un poco su awkexpresión
Rahul

1
@Rahul Claro. He agregado una explicación del código awk.
John1024

1
Gracias John. Esto es exactamente lo que estaba buscando.
gibies

Mucho simple para eliminar el bloque y luego agregar uno nuevo a la última línea:sed '/3/,/4/d;/5/cNew Code' awk '/5/{print "New Code"}/3/,/5/{next}1'
Costas

1

Basado en la discusión anterior, llego a una solución usando ex

seq 15 > test1.txt
ex test1.txt << EOEX
/^7/,/^9/c
abcd
123
.xyz
hfr4
.
w!
q
EOEX
cat test1.txt

Lo anterior da

1
2
3
4
5
6
abcd
123
.xyz
hfr4
10
11
12
13
14
15

Gracias g-man por informarme sobre el comando de cambio y dar aclaraciones sobre dot. Ambos sedy excomparten una sintaxis casi similar para el comando de cambio (también para el comando eliminar, agregar comando, etc.).

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.