Reemplazar un marcador en un archivo de texto con texto nuevo arbitrario

Reemplazar un marcador en un archivo de texto con texto nuevo arbitrario

Tengo un archivo que contiene texto y, en una sola línea, un marcador que indica dónde se debe agregar contenido nuevo.

foo
bar
%SUBSTITUTE%
foo

La línea sustituta de línea debe reemplazarse por una nueva cadena multilínea text="some text"(tenga en cuenta que no conozco la cadena; por ejemplo, puede ser el resultado de leer un archivo text=cat "file"``). El resultado del reemplazo debe decir

foo
bar
some text
%SUBSTITUTE%
foo

Tenía una versión funcional que perldejó de funcionar (aparentemente debido al cambio de versión de Perl). Ahora estoy probando utilidades estándar como try sedpara reemplazar la línea. Tengo algunos problemas con esto, porque la cadena que se va a pegar puede contener caracteres arbitrarios, incluidas barras invertidas, etc. No quiero escapar de ellos antes de pegar.

¿Existe una forma segura de hacerlo que funcione con herramientas estándar? Las otras dudas que encuentro para el problema son soluciones particulares donde se conoce el texto a pegar.

Respuesta1

Si tiene la versión GNU de sed, debería poder usar el rcomando para leer e insertar contenido nuevo de un archivo y luego eliminar la línea de marcador, por ejemplo.

sed '/%SUBSTITUTE%/{
r path/to/newcontent
d
}' file

Si desea conservar el %SUBSTITUTE%marcadordespuésla inserción, eso es complicado porque hagas lo que hagas, la rextensión GNU pone en cola el contenido del archivo hasta quefindel ciclo del patrón actual (conservándoloantessolo sería cuestión de eliminar el dcomando). Probablemente la forma más sencilla sea agregarlo al newcontentarchivo: puedes hacerlo sobre la marcha como

sed '/%SUBSTITUTE%/{
r /dev/stdin
d
}' file < <(sed '$a %SUBSTITUTE%' path/to/newcontent)

Adoptando un enfoque completamente diferente, podría dividir el primer archivo %SUBSTITUTE%y luego incluir el nuevo contenido.

csplit -s file '/%SUBSTITUTE%/'
cat xx00 newcontent xx01

También puede hacer un readbucle bash sobre las líneas del primer archivo y capturar el nuevo archivo de contenido cuando coincida con la cadena del marcador; sin embargo, me han golpeado los nudillos por sugerir bucles de lectura para el procesamiento de texto en este foro. Lamentablemente, ninguno de los dos proporciona una solución implementada.

Respuesta2

Aquí hay otra forma de usar ed.
Inserte todo el contenido de FILEantes del marcador (es decir, antes de la línea que contiene %SUBSTITUTE%):

ed -s originalfile <<< $'/%SUBSTITUTE%/- r FILE\nw\nq'

donde:
/%SUBSTITUTE%/: establece la dirección en la primera línea coincidente %SUBSTITUTE%
-o -1: desplaza la dirección una línea antes
r FILE: Reads FILE to after the addressed line.
w: escribe en originalfile(reemplazar con ,ppara simplemente imprimir el contenido en lugar de escribir)
q: sale del editor

Reemplazar FILEcon !echo "$TEXT"insertará el contenido de $TEXTantes del marcador:

export TEXT
ed -s originalfile <<'IN'
/%SUBSTITUTE%/-1 r !echo "$TEXT"
w
q
IN

Respuesta3

Encontré la siguiente solución basada en awk:

text=`cat "filepaste"`
export text;
<"$file" awk '
    BEGIN {REPLACE=ENVIRON["text"] "\n%SUBSTITUTE%" }
    {gsub(/^%SUBSTITUTE%$/, REPLACE); print}
'

Aquí "filepaste" contiene el contenido que se sustituirá por %SUBSTITUTE%. Una ventaja es que se puede actuar sobre esta cadena utilizando diferentes herramientas de shell sin la necesidad de guardarla nuevamente en un archivo. Leer la awkvariable REPLACEde la variable de entorno evita la expansión de caracteres de escape en text.

Respuesta4

Como tenías una solución Perl, aquí tienes otra. Estoy usando rep.txtpara almacenar el reemplazo:

$ perl -pe '$re=`cat rep.txt`; chomp($re); s/%SUBSTITUTE%\n/$re/' file.txt
foo
bar
multi
  line
string
foo

Eso lee cada línea del archivo de destino ( file.txt), luego le aplica el script proporcionado -ee imprime ( -p).

información relacionada