Como posso excluir todo o texto entre colchetes aninhados em um arquivo de texto com várias linhas?

9

Esta pergunta vem de Como posso excluir todo o texto entre colchetes em um arquivo de texto com várias linhas? (da mesma forma, mas sem os requisitos para aninhamento).

Exemplo:

This is {
{the multiline
text} file }
that wants
{ to {be
changed}
} anyway.

Deve se tornar:

This is 
that wants
 anyway.

É possível fazer isso com algum tipo de comando bash de uma linha (awk, sed, perl, grep, cut, tr ... etc)?

Sopalajo de Arrierez
fonte

Respostas:

13
$ sed ':again;$!N;$!b again; :b; s/{[^{}]*}//g; t b' file3
This is 
that wants
 anyway.

Explicação:

  • :again;$!N;$!b again

    Isso lê o arquivo inteiro.

    :againé um rótulo. Nlê na próxima linha e $!Nlê na próxima linha com a condição de que ainda não estamos na última linha. $!b againvolta ao againrótulo com a condição de que essa não seja a última linha.

  • :b

    Isso define um rótulo b.

  • s/{[^{}]*}//g

    Isso remove o texto entre chaves, desde que o texto não contenha chaves internas.

  • t b

    Se o comando substituto acima resultou em uma alteração, volte para o rótulo b. Dessa maneira, o comando substituto é repetido até que todos os grupos de chaves sejam removidos.

John1024
fonte
3

Uma abordagem Perl:

$ perl -F"" -a00ne 'for (@F){$i++ if /{/; $i||print; $i-- if /}/}' file
This is 
that wants
 anyway

Explicação

  • -a: ativa a divisão automática no delimitador de arquivo fornecido por -Fna @Fmatriz.
  • -F"": define o separador do campo de entrada como vazio, o que resultará em cada elemento @Fcomo um dos caracteres de entrada.
  • -00: ativa o "modo de parágrafo", onde uma "linha" é definida como dois caracteres consecutivos de nova linha. Isso significa que o arquivo inteiro nesse caso será tratado como uma única linha. Se o seu arquivo pode ter muitos parágrafos e os colchetes podem abranger vários parágrafos, use-o -0777.
  • -ne: leia um arquivo de entrada e aplique o script fornecido por -ecada linha.

O script em si é realmente bastante simples. Um contador é incrementado em um toda vez que a {é visto e decrementado em um para cada }. Isso significa que quando o contador é 0, não estamos entre colchetes e devemos imprimir:

  • for (@F){}: faça isso para cada elemento de @F, cada caractere na linha.
  • $i++ if /{/;: incrementa $iem um se esse caractere for um{
  • $i||print;: imprime a menos que $iesteja definido (0 conta como não definido).
  • $i-- if /}/: decrementa $ipor um se esse caractere for um}
terdon
fonte