
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 perl
qual parou de funcionar (aparentemente devido à mudança de versão do Perl). Agora estou tentando utilitários padrão como tr
e sed
para 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 r
comando 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 r
extensã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 d
comando). Provavelmente a maneira mais simples é anexá-lo ao newcontent
arquivo: 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 read
loop 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 FILE
antes 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.
w
escreve para originalfile
(substitua por ,p
apenas imprimir o conteúdo em vez de escrever)
q
: sai do editor
Substituir FILE
por !echo "$TEXT"
inserirá o conteúdo de $TEXT
antes 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 awk
variável REPLACE
da 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.txt
para 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 -e
a ele e imprime ( -p
).