Como substituir o texto entre dois padrões em linhas diferentes?

5

Eu tenho vários arquivos com texto que precisam ser substituídos. O texto começa e termina com o mesmo padrão de cada vez, mas o conteúdo entre os padrões é variável. Os padrões podem aparecer no meio das linhas e o conteúdo entre eles geralmente se estende por várias linhas.

Haverá apenas uma única ocorrência do padrão inicial e final em cada arquivo.

Eu preciso de um método de linha de comando para substituir o texto entre os padrões, incluindo os próprios padrões. Saída para um novo arquivo ou edição no local é bom.

Um comando que opera em um único arquivo funcionará, pois eu posso percorrer os arquivos e aplicar o comando sozinho. Eu tentei uma solução sed , mas consegui substituir apenas linhas inteiras.

Um exemplo de texto seria:

Cable Type ID:135, Installation ID:62, Alpha Conductor Origin:
Tolerance Report B74 - 3rd June 1996, Beta Conductor Origin: 
Tolerance Report B74 - 3rd June 1996, Phase Conductor Size: 
45mm, Security: Security-Start Bs86gKI-734Lw#32_nP/5589Zfb8Wj-
sW93j9b Security-End, Location ID:889, Protective Earth Size:
67mm, Protective Earth Max Current (A): 4, Overload Time...

O padrão de início é Security-Start e o padrão final é Security-End . Eu quero substituir os padrões e tudo mais com a palavra REDACTED .

Eu gostaria que a saída fosse:

Cable Type ID:135, Installation ID:62, Alpha Conductor Origin:
Tolerance Report B74 - 3rd June 1996, Beta Conductor Origin: 
Tolerance Report B74 - 3rd June 1996, Phase Conductor Size: 
45mm, Security: REDACTED, Location ID:889, Protective Earth Size:
67mm, Protective Earth Max Current (A): 4, Overload Time...

Por favor, note que o texto entre os dois padrões pode ser tão longo que abrange várias linhas, é de comprimento bastante aleatório. Isso não está claro no exemplo acima

Qualquer idioma que esteja disponível por padrão em um sistema Ubuntu estará bem. Meus primeiros pensamentos são 'sed' ou 'awk', mas o que você estiver confortável ficará bem.

    
por Arronical 18.05.2017 / 13:09

4 respostas

8

Isso deve funcionar para você:

sed -e '/Security-Start/{ N; s/Security-Start.*Security-End/REDACTED/ }'
  • /Security-Start/ procura por "Início de Segurança"
  • Se você achou: "N;" significa anexar a próxima linha.
  • e faça a substituição s/Security-Start.*Security-End/REDACTED/ no resultado final.

Por mais de duas linhas use esta:

sed -n '1h; 1!H; ${ g; s/Security-Start.*Security-End/REDACTED/p }'

Leia aqui

    
por Ravexina 18.05.2017 / 13:34
8

Se os arquivos não forem muito grandes, você poderá usar o perl no modo slurp :

$ perl -0777 -pe 's/Security-Start.*Security-End/REDACTED/s' file 
Cable Type ID:135, Installation ID:62, Alpha Conductor Origin:
Tolerance Report B74 - 3rd June 1996, Beta Conductor Origin: 
Tolerance Report B74 - 3rd June 1996, Phase Conductor Size: 
45mm, Security: REDACTED, Location ID:889, Protective Earth Size:
67mm, Protective Earth Max Current (A): 4, Overload Time...

O parâmetro de linha de comando -0777 desativa efetivamente o separador de registro para que todo o arquivo seja slurped. O modificador s regex faz com que o perl inclua caracteres de nova linha em . , fazendo com que a expressão corresponda às linhas.

Como alternativa, com um loop sed:

$ sed '/Security-Start/ {:a; $!N; s/Security-Start.*Security-End/REDACTED/; t; ba}' file
Cable Type ID:135, Installation ID:62, Alpha Conductor Origin:
Tolerance Report B74 - 3rd June 1996, Beta Conductor Origin: 
Tolerance Report B74 - 3rd June 1996, Phase Conductor Size: 
45mm, Security: REDACTED, Location ID:889, Protective Earth Size:
67mm, Protective Earth Max Current (A): 4, Overload Time...

Com o GNU sed, você pode substituir t; ba (ramifique-se na substituição bem-sucedida; caso contrário, ramifique para :a ) por Ta (ramifique para :a em un substituição bem-sucedida ).

    
por steeldriver 18.05.2017 / 13:59
4

Uma abordagem mais manual seria substituir todo o caractere de nova linha no arquivo de entrada por NULLs, usar uma regex simples perl non-greedy para fazer a substituição e depois colocar as novas linhas de volta:

$ tr '\n' '
$ tr '\n' '%pre%' < file | 
    perl -pe 's/Security-Start.*?Security-End/Security: REDACTED/g' |
        tr '%pre%' '\n'
Cable Type ID:135, Installation ID:62, Alpha Conductor Origin:
Tolerance Report B74 - 3rd June 1996, Beta Conductor Origin: 
Tolerance Report B74 - 3rd June 1996, Phase Conductor Size: 
45mm, Security: Security: REDACTED, Location ID:889, Protective Earth Size:
67mm, Protective Earth Max Current (A): 4, Overload Time...
' < file | perl -pe 's/Security-Start.*?Security-End/Security: REDACTED/g' | tr '%pre%' '\n' Cable Type ID:135, Installation ID:62, Alpha Conductor Origin: Tolerance Report B74 - 3rd June 1996, Beta Conductor Origin: Tolerance Report B74 - 3rd June 1996, Phase Conductor Size: 45mm, Security: Security: REDACTED, Location ID:889, Protective Earth Size: 67mm, Protective Earth Max Current (A): 4, Overload Time...
    
por terdon 18.05.2017 / 17:55
1

Veja como você pode fazer isso com o awk:

awk -v RS='Security-Start.*Security-End' -v ORS= '1;NR==1{printf "REDACTED"}' file
    
por user000001 18.05.2017 / 21:03