pega texto por padrão com tag de início e fim em múltiplas linhas [duplicado]

10

Eu quero pegar várias linhas em um arquivo com um padrão que tenha uma tag inicial e uma final de forma imprópria.

Por exemplo, eu tenho a seguinte entrada:

arquivo.txt

START
test1
test2
foo
END
some
more text

START
test3
bar
test4
test5
END
even more

START
baz
test6
END

Agora eu quero procurar por bar e imprimir tudo entre START e END , para que eu tenha:

START
test3
bar
test4
test5
END

O que eu tenho até agora é o seguinte comando grep:

grep -Pzo '(?s)START.*?bar.*?END' file.txt

O problema é que essa expressão é gananciosa e imprime:

START      # starts at first "START"-tag, not the next one
test1      #
test2      #
foo        #
END        #
some       #
more text  #

START
test3
bar
test4
test5
END

Não é feito com o grep flags - antes do contexto / - after-context , porque a contagem de linhas antes e depois pode ser diferente.

A ferramenta usada pelo processamento de texto não importa. Deve funcionar em um sistema geral da RedHat. Além disso, quanto mais rápido a ferramenta agarra as linhas, melhor será. Porque eu tenho grandes arquivos de log de aproximadamente 150MB.

Alguém pode me dizer como alcançar meu objetivo da melhor maneira?

Atualização:

Ok, eu entendi. Eu só tive que pensar em como construir meu comando a partir do link don_crissti s. Aqui está a solução:

ed -s file.txt <<< $'g/bar/?START?,/END/p\nq\n'

Muito obrigado por toda sua ajuda muito rápida!

E sim, finalmente é uma duplicata ...

    
por bobbel 07.10.2015 / 17:02

3 respostas

4

Eu acho que o seu problema é que as suas correspondências não gananciosas ainda podem engolir mais do que você quer, por exemplo. END se START s. Isso parece funcionar:

grep -Pzo '(?s)START(?:(?!END).)*?bar(?:(?!START).)*?END' file.txt

Ele abrange todos os casos em seu exemplo e está completo se você >> file.txt

bar
START
test7
END

Ainda funciona.

    
por 07.10.2015 / 18:30
2

Eu usaria awk, onde você pode especificar o separador de registro. Se o separador de registro for "END" (em sua própria linha), procure o registro que contém "bar":

awk 'BEGIN {RS = ORS = "\nEND\n"} /bar/' file.txt

Manipulação de texto que aparece entre os marcadores START e END. Essa mudança parece hacky, mas funciona para este cenário: usando END como separador de registro, remova qualquer texto antes da palavra-chave START

awk '
    BEGIN {RS = ORS = "\nEND\n"} 
    {sub(/^.*\nSTART\n/, "START\n")} 
    /bar/
' file.txt

Isso pode não dar os resultados desejados se "START" puder aparecer mais de uma vez antes do END

foo
START
hello
START
bar
world
END
baz

será exibido como

START
bar
world
END
    
por 07.10.2015 / 17:19
1
perl -nE 'BEGIN {$/="\nEND\n"} say /(START.*test.*)/s'

Como @bobbel aponta, substitua say por print para evitar separadores de linha vazios.

    
por 07.10.2015 / 19:08