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

5

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

Estou impressionado com o Como posso usar o sed para substituir uma sequência de várias linhas? respondida por antak e Substituição de várias linhas respondida por Bruce Ediger

Mas tenho alguns problemas em usá-los.

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

  2. Eu sei que sed pode ser usado com o recurso de bloqueio 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 um novo texto (código) repetidamente para cada fluxo. Portanto, eu tenho que fazer o bloco visual como um fluxo, usando algo semelhante ao sugerido pelo antak antes, mas para o bloco (não para o arquivo inteiro).

  1. Como mencionado por Bruce Ediger acrescenta recurso de ex que começa com a end com . (ponto) pode ser tentado, 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 dd 'número de linhas' 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 fazê-lo?

por gibies 16.08.2016 / 08:29

3 respostas

7

Usando o GNU sed

Para substituir as linhas que iniciam uma linha que corresponde a 3 e continuar para uma linha que corresponde a 5 com 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 se a linha corresponde a 5 (o que significa que esta é a última linha do grupo) e, em caso afirmativo, fazemos a substituição para inserir New Code . Se a substituição foi feita, o comando t informará ao sed para ir até o final dos comandos (nesse caso, New Code é impresso). Caso contrário, o comando d informará ao sed para excluir a linha.

Todas as outras linhas são impressas normalmente.

Para alterar o arquivo, a opção -i pode ser usada:

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

Usando o 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 as linhas no intervalo /3/,/5/ especialmente. Para linhas nesse intervalo, testamos se f é zero (ou seja, se !f for true) e, em caso afirmativo, imprimimos New Code e definimos f para 1 e, em seguida, ignoramos o restante dos comandos e pule para a linha next .

Para linhas fora do intervalo /3/,/5/ , nenhum salto é feito e o 1 faz com que a linha seja impressa. Mais detalhadamente, 1 é uma condição. Como 1 nã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 impressão na linha.

Para alterar um arquivo, a opção -i inplace pode ser usada com o GNU awk 4.1 ou superior:

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

Sugiro usar o comando c hange (que é essencialmente um elete d acoplado com um a ppend, embora o acréscimo seja aplicado somente para a última linha no intervalo que é exatamente o que você deseja aqui):

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

Aqui está usando a sintaxe do GNU sed para edição no local ( -i ). Esse comando c é padrão e portátil. GNU sed suporta:

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

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

Os caracteres de nova linha e de barra invertida devem ter escape (com contrabarra) no texto de substituição.

    
por 16.08.2016 / 08:50
0

Com base na discussão acima, descubro 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 texto acima dá

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

Obrigado g-man para me informar sobre o comando de mudança e dar esclarecimentos sobre o ponto. Tanto sed quanto ex compartilham uma sintaxe quase semelhante para o comando change (também para o comando delete, comando append, etc).

    
por 16.08.2016 / 10:23

Tags