Como posso excluir tudo entre dois marcadores em um arquivo?

Como posso excluir tudo entre dois marcadores em um arquivo?

Eu tenho um texto em um arquivo de texto, onde quero que tudo o que está entre as strings goste \{{[}e {]}\}seja excluído - incluindo essas próprias strings. Essas duas cordaspodeestão em linhas diferentes e também na mesma linha. Em ambos os casos, sobrea linha em que \{{[}está o início, não quero que o texto antes, ou seja, à esquerda, seja excluído - e o mesmo vale para o texto depois {]}\}.

Aqui está um exemplo: Dado um arquivo de texto com o conteúdo

Bla Bla bla bla \{{[} more bla bla
even more bla bla bla bla. 

A lot of stuff might be here.

Bla bla {]}\} finally done.

Nonetheless, the \{{[} show {]}\} goes on.

o script deve retornar outro arquivo de texto com o conteúdo

Bla Bla bla bla  finally done.

Nonetheless, the  goes on.

Infelizmente, essa tarefa aparentemente simples acabou sendo muito difícil para mim sed. estou feliz comqualquersolução em qualquer idioma, desde que eu não precise instalar nada na minha máquina Linux padrão (C e algum java já está instalado).

Responder1

Com perl:

perl -0777 -pe 's/\Q\{{[}\E.*?\Q{]}\}\E//gs'

Observe que toda a entrada é carregada na memória antes de ser processada.

\Qsomething\Edeve somethingser tratado como uma string literal e não como uma expressão regular.

Para modificar um arquivo normal no local, adicione a -iopção:

perl -0777 -i -pe 's/\Q\{{[}\E.*?\Q{]}\}\E//gs' file.txt

Com GNU awkou mawk:

awk -v 'RS=\\\\\\{\\{\\[}|\\{\\]}\\\\}' -v ORS= NR%2

Lá, estamos definindo oseparador de registroscomo um desses marcadores de início ou fim (somente gawke mawksuporte RSsendo um regexp aqui). Mas precisamos escapar dos caracteres que são operadores regexp (barra invertida, {, [) e também da barra invertida mais uma vez porque é especial em argumentos para -v(usado para coisas como \n, \b...), daí as inúmeras barras invertidas.

Então tudo o que precisamos fazer é imprimir todos os outros registros. NR%2seria 1(verdadeiro) para cada registro ímpar.

Para ambas as soluções, presumimos que os marcadores correspondam e que as seções não estejam aninhadas.

Para modificar o arquivo no local, com versões recentes do GNU awk, adicione a -i /usr/share/awk/inplace.awkopção ¹.


¹não use-i inplaceas gawktenta carregar primeiro a inplaceextensão (como inplaceou inplace.awk) do diretório de trabalho atual, onde alguém poderia ter plantado malware. O caminho da inplaceextensão fornecida gawkpode variar de acordo com o sistema, consulte a saída degawk 'BEGIN{print ENVIRON["AWKPATH"]}'

Responder2

sed   -e:t -e'y/\n/ /;/\\{{\[}/!b'               \
      -e:N -e'/\\{{\[.*{\]}\\}/!N'               \
           -e's/\(\\{{\[}\).*\n/\1/;tN'          \
           -e'y/ /\n/;s/\\{{\[}/& /;ts'          \
      -e:s -e's/\(\[} [^ ]*\)\({\]}\\}\)/\1 \2/' \
      -ets -e's/..... [^ ]* .....//;s/ //g;bt'   \
<<""
#Bla Bla {]}\} bla bla \{{[} more bla bla
#even more bla bla bla bla. \{{[} 
#
#A lot of stuff might be here.
#hashes are for stupid syntax color only
#Bla bla {]}\} finally {]}\} done.
#
#Nonetheless, the \{{[} show {]}\} goes \{{[} show {]}\} on.

#Bla Bla {]}\} bla bla  finally {]}\} done.
#
#Nonetheless, the  goes  on.

Aqui está uma maneira muito melhor, no entanto. Muito menos substituições, e as que são feitas são para alguns caracteres por vez, e não .*o tempo todo. Praticamente, o único tempo .*usado é para limpar o espaço padrão do espaço intermediário, quando o primeiro início ocorrido está definitivamente emparelhado com o primeiro final seguinte. Todo o resto do tempo sedsimplesmente Dexclui o necessário para chegar ao próximo delimitador que ocorre. Don me ensinou isso.

sed -etD -e:t -e'/\\{{\[}/!b'  \
    -e's//\n /;h;D'       -e:D \
    -e'/^}/{H;x;s/\n.*\n.//;}' \
    -ett    -e's/{\]}\\}/\n}/' \
    -e'/\n/!{$!N;s//& /;}' -eD \
<<""
#Bla Bla {]}\} bla bla \{{[} more bla bla
#even more bla bla bla bla. \{{[} 
#
#A lot of stuff might be here.
#hashes are for stupid syntax color only
#Bla bla {]}\} finally {]}\} done.
#
#Nonetheless, the \{{[} show {]}\} goes \{{[} show {]}\} on.

#Bla Bla {]}\} bla bla  finally {]}\} done.
#
#Nonetheless, the  goes  on.

No entanto, os \nescapes ewline do RHS podem precisar ser substituídos por novas linhas de escape literais com barra invertida.

Aqui está uma versão mais genérica:

#!/usr/bin/sed -f
####replace everything between START and END
   #branch to :Kil if a successful substitution
   #has already occurred. this can only happen
   #if pattern space has been Deleted earlier
    t Kil
   #set a Ret :label so we can come back here
   #when we've cleared a START -> END occurrence
   #and check for another if need be
    :Ret
   #if no START, don't
    /START/!b
   #sigh. there is one. get to work. replace it
   #with a newline followed by an S and save
   #a copy then Delete up to our S marker.
    s||\
S|
    h;D
   #set the :Kil label. we'll come back here from now
   #on until we've definitely got END at the head of
   #pattern space.
    :Kil
   #do we? 
    /^E/{
       #if so, we'll append it to our earlier save
       #and slice out everything between the two newlines
       #we've managed to insert at just the right points        
        H;x
        s|\nS.*\nE||
    }
   #if we did just clear START -> END we should
   #branch back to :Ret and look for another START
    t Ret
   #pattern space didnt start w/ END, but is there even
   #one at all? if so replace it w/ a newline followed
   #by an E so we'll recognize it at the next :Kil
    s|END|\
E|
   #if that last was successful we'll have a newline
   #but if not it means we need to get the next line
   #if the last line we've got unmatched pairs and are
   #currently in a delete cycle anyway, but maybe we
   #should print up to our START marker in that case?
    /\n/!{
       #i guess so. now that i'm thinking about it
       #we'll swap into hold space, and Print it
        ${  x;P;d
        }
       #get next input line and add S after the delimiting
       #newline because we're still in START state. Delete
       #will handle everything up to our marker before we
       #branch back to :Kil at the top of the script
        N
        s||&S|
    }
   #now Delete will slice everything from head of pattern space
   #to the first occurring newline and loop back to top of script.
   #because we've definitely made successful substitutions if we
   #have a newline at all we'll test true and branch to :Kil 
   #to go again until we've definitely got ^E
    D

...sem comentários...

#!/usr/bin/sed -f
    t Kil
    :Ret
    /START/!b
    s||\
S|
    h;D
    :Kil
    /^E/{
        H;x
        s|\nS.*\nE||
    }
    t Ret
    s|END|\
E|
    /\n/!{
        ${  x;P;d
        }
        N
        s||&S|
    }
    D

Copiei a versão comentada para minha área de transferência e fiz:

{ xsel; echo; } >se.sed
chmod +x se.sed
./se.sed <se.sed

#!/usr/bin/sed -f
####replace everything between
   #branch to :Kil if a successful substitution
   #has already occurred. this can only happen
   #if pattern space has been Deleted earlier
    t Kil
   #set a Ret :label so we can come back here
   #when we've cleared a  occurrence
   #and check for another if need be
    :Ret
   #if no  at the head of
   #pattern space.
    :Kil
   #do we?
    /^E/{
       #if so, we'll append it to our earlier save
       #and slice out everything between the two newlines
       #we've managed to insert at just the right points
        H;x
        s|\nS.*\nE||
    }
   #if we did just clear  we should
   #branch back to :Ret and look for another , but is there even
   #one at all? if so replace it w/ a newline followed
   #by an E so we'll recognize it at the next :Kil
    s|END|\
E|
   #if that last was successful we'll have a newline
   #but if not it means we need to get the next line
   #if the last line we've got unmatched pairs and are
   #currently in a delete cycle anyway, but maybe we
   #should print up to our

Responder3

Se o seu arquivo for test.txt você pode usar:

sed ':a;N;$!ba;s/\n/ /g' test.txt|sed 's/\\{{\[}.*{\]}\\}//' 

o primeiro sed remove todas as novas linhas, o segundo remove o texto dentro das tags.

Não sei se você precisa de uma solução mais geral

informação relacionada