Como posso excluir todo o texto entre colchetes aninhados em um arquivo de texto de múltiplas linhas?

8

This question comes from How can I delete all text between curly brackets in a multiline text file? (just the same, but without the requirements for nesting).

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)?

    
por Sopalajo de Arrierez 09.11.2014 / 00:32

2 respostas

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

Explicação:

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

    Isto lê todo o arquivo.

    :again é um rótulo. N lê na próxima linha e $!N lê na próxima linha na condição de que ainda não estamos na última linha. $!b again ramifica de volta para o rótulo again 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, retorne ao rótulo b . Desta forma, o comando substituto é repetido até que todos os grupos de chaves sejam removidos.

por 09.11.2014 / 00:38
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 arquivos fornecido por -F na matriz @F .
  • -F"" : define o separador do campo de entrada como vazio, o que resultará em cada elemento de @F sendo um dos caracteres de entrada.
  • -00 : ativa o "modo de parágrafo", onde uma "linha" é definida como dois caracteres de nova linha consecutivos. Isso significa que o arquivo inteiro, neste caso, será tratado como uma única linha. Se o seu arquivo pode ter muitos parágrafos e os colchetes podem se estender por vários parágrafos, use -0777 .
  • -ne : leia um arquivo de entrada e aplique o script fornecido por -e em cada linha.

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

  • for (@F){} : faça isso para cada elemento de @F , cada caractere na linha.
  • $i++ if /{/; : increment $i por um se esse caractere for um {
  • $i||print; : imprime a menos que $i seja definido (0 conta como não definido).
  • $i-- if /}/ : decrementar $i em um se esse caractere for um }
por 09.11.2014 / 03:52