Substitua um marcador em um arquivo de texto por um novo texto arbitrário

Substitua um marcador em um arquivo de texto por um novo texto arbitrário

Eu tenho um arquivo que contém algum texto e - em uma única linha - um marcador que indica onde o novo conteúdo deve ser adicionado

foo
bar
%SUBSTITUTE%
foo

O substituto da linha line deve ser substituído por uma nova string multilinha text="some text"(observe que não conheço a string, ela pode, por exemplo, ser o resultado da leitura de um arquivo text=cat "file"``). O resultado da substituição deve ser lido

foo
bar
some text
%SUBSTITUTE%
foo

Eu tinha uma versão funcional baseada na perlqual parou de funcionar (aparentemente devido à mudança de versão do Perl). Agora estou tentando utilitários padrão como tre sedpara substituir a linha. Estou tendo alguns problemas com isso, porque a string a ser colada pode conter caracteres arbitrários, incluindo barras invertidas, etc. Não quero escapar deles antes de colar.

Existe uma maneira segura de fazer isso que funcione com ferramentas padrão? As outras questões que encontro para o problema são soluções particulares onde o texto a ser colado é conhecido.

Responder1

Se você possui a versão GNU do sed, poderá usar o rcomando para ler e inserir novo conteúdo de um arquivo e, em seguida, excluir a linha do marcador, por exemplo

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

Se você quiser manter o %SUBSTITUTE%marcadordepoisa inserção, isso é complicado porque faça o que fizer, a rextensão GNU coloca o conteúdo do arquivo na fila até que ofimdo ciclo padrão atual (mantendo-oantesseria apenas uma questão de remover o dcomando). Provavelmente a maneira mais simples é anexá-lo ao newcontentarquivo: você poderia fazer isso rapidamente, como

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

Adotando uma abordagem completamente diferente, você poderia dividir o primeiro arquivo %SUBSTITUTE%e depois inserir o novo conteúdo

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

Você também pode fazer um readloop bash sobre as linhas do primeiro arquivo e cat o novo arquivo de conteúdo quando corresponder à string do marcador - no entanto, levei uma pancada nos nós dos dedos por sugerir loops de leitura para processamento de texto neste fórum. Infelizmente, nenhum deles fornece uma solução local.

Responder2

Aqui está outra maneira de usar ed.
Insira todo o conteúdo FILEantes do marcador (ou seja, antes da linha que contém %SUBSTITUTE%):

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

onde::
/%SUBSTITUTE%/define o endereço na correspondência da primeira linha %SUBSTITUTE%
-ou -1: desloca o endereço uma linha antes
r FILE:: Reads FILE to after the addressed line.
wescreve para originalfile(substitua por ,papenas imprimir o conteúdo em vez de escrever)
q: sai do editor

Substituir FILEpor !echo "$TEXT"inserirá o conteúdo de $TEXTantes do marcador:

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

Responder3

Encontrei a seguinte solução com base em awk:

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

Aqui "filepaste" contém o conteúdo a ser substituído %SUBSTITUTE%. Uma vantagem é que essa string pode ser executada usando diferentes ferramentas de shell sem a necessidade de salvá-la em um arquivo. Ler a awkvariável REPLACEda variável de ambiente evita a expansão de caracteres de escape em text.

Responder4

Já que você tinha uma solução Perl, aqui está outra. Estou usando rep.txtpara armazenar a substituição:

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

Ele lê cada linha do arquivo de destino ( file.txt), depois aplica o script fornecido por -ea ele e imprime ( -p).

informação relacionada