Como posso usar sed ou ex para substituir um bloco (código de várias linhas) por um novo bloco de texto (código)?

9

Eu tenho que substituir um grande bloco de texto (código de script de shell) em um arquivo por outro bloco de texto.

Estou impressionado com o Como posso usar o sed para substituir uma string de várias linhas? respondidas por antak e Multi-line replace respondidas por Bruce Ediger

Mas tenho alguns problemas em usá-los.

  1. O Antak já mencionou em sua resposta que o streaming de arquivo inteiro ( 1h;2,$H;$!d;g;) para o buffer não é aconselhável para arquivos grandes, porque sobrecarrega a memória.

  2. Eu sei que sedpode ser usado com o recurso de bloco para preservar o texto fora do bloco inalterado. Eu quero usar esse recurso. Mas se eu usar,

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

ele irá inserir novo texto (código) repetidamente para cada fluxo. Portanto, eu tenho que fazer o bloco visual como um fluxo, usando algo semelhante ao sugerido por antak anteriormente, mas para o bloco (não para o arquivo inteiro).

  1. Conforme mencionado por Bruce Ediger, é possível tentar o recurso de acréscimo exque começa com afinal com .(ponto), mas meu novo texto (código) contém linhas que começam com ponto, que pode ser considerado como o ponto da sintaxe de acréscimo. Como posso usá-lo nesta situação?

  2. ex's dd' number of lines 'pode excluir várias linhas, mas se eu tiver um bloco entre / marker1 / e / marker2 / com o número de linhas não fixas (variando) será substituído por um novo texto (código), como fazer isto ?

gibies
fonte
Acho que o uso do termo "bloqueio visual" é incomum e confuso. Você quer dizer simplesmente um bloco (grupo) de linhas consecutivas e completas?
Scott
Sim, estou editando a pergunta para evitar confusão.
Gibies
Qual é a fonte do texto de substituição? Está em um arquivo ou é "apenas na mente dos programadores"?
don_crissti
Isto é para correção situacional de um script de shell. Portanto, a fonte do texto de substituição é a mente do programador (ou um script mestre no qual sedou excomando funciona).
Gibies
Don Crissti Eu usaria ro comando read file para copiar o texto de outro arquivo, se a origem do texto de substituição estiver em um arquivo.
Gibies

Respostas:

10

Eu sugiro usar o c comando hange (que é essencialmente um d elete juntamente com um um ppend, embora o acréscimo é aplicado somente para a última linha na faixa que é exatamente o que você quer aqui):

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

Aqui, usando seda sintaxe do GNU para edição in-loco ( -i). cCaso contrário, esse comando é padrão e portátil. O GNU sedsuporta:

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

como uma extensão não padrão.

Os caracteres de nova linha e barra invertida devem ser escapados (com barra invertida) no texto de substituição.

G-Man diz que 'restabelece Monica'
fonte
O comando change funciona bem para ambos sede para os exmesmos. Muito obrigado.
Gibies
Existe alguma maneira de evitar "\" e usar aqui o documento sedcomo no caso da exsintaxe sugerida por Bruce Ediger na substituição de várias linhas .
Gibies
Você não precisaria das barras invertidas se usasse o rcomando discutido em outras respostas para essa pergunta. Não tenho certeza de como você usaria um documento aqui sed.
G-Man diz 'Reinstate Monica'
8

Usando o GNU sed

Para substituir linhas iniciando uma correspondência de linha 3e continuando para uma linha correspondente 5a New Code:

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

Para linhas no intervalo /3/,/5/, testamos para ver se a linha corresponde 5(o que significa que esta é a última linha do grupo) e, nesse caso, fazemos a substituição para inserir New Code. Se a substituição foi feita, o tcomando diz ao sed para pular para o final dos comandos (nesse caso, New Codeé impresso). Caso contrário, o dcomando diz ao sed para excluir a linha.

Todas as outras linhas são impressas normalmente.

Para alterar o arquivo no local, a -iopção pode ser usada:

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

Usando awk

Para fazer o mesmo usando o awk:

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

O comando awk trata especialmente as linhas no intervalo /3/,/5/. Para linhas nesse intervalo, testamos para ver se fé zero (ou seja, se !fé verdade) e, se sim, imprimimos New Codee configuramos fcomo 1 e depois pulamos o restante dos comandos e pulamos para a nextlinha.

Para linhas fora do intervalo /3/,/5/, nenhum salto é feito e 1faz com que a linha seja impressa. Em mais detalhes, 1é uma condição. Como 1não é zero, a condição é avaliada como verdadeira. Como nenhuma ação está associada à condição, é executada a ação padrão que é imprimir a linha. Assim, 1é uma abreviação para print-the-line.

Para alterar um arquivo, a -i inplaceopção pode ser usada com o GNU awk4.1 ou superior:

awk -i inplace '/3/,/5/{if (!f)print "New Code"; f=1; next} 1' file
John1024
fonte
legal. Eu não sou bom em awk, você poderia explicar pouco sua awkexpressão
Rahul
1
@Rahul Sure. Eu adicionei uma explicação do código awk.
precisa saber é o seguinte
1
Obrigado John Isso é exatamente o que eu estava procurando.
Gibies
Muito simples de remover o bloco e adicionar um novo à última linha:sed '/3/,/4/d;/5/cNew Code' awk '/5/{print "New Code"}/3/,/5/{next}1'
Costas
1

Com base na discussão acima, eu venho com uma solução usando ex

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

O exposto acima

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

Obrigado g-man por me informar sobre o comando change e dar esclarecimentos sobre o dot. Ambos sede exshare sintaxe quase semelhante para o comando de mudança (também para comando de exclusão, o comando de acréscimo, etc).

gibies
fonte