Extraia parte do arquivo de texto da primeira ocorrência de uma string até a primeira ocorrência de outra

Extraia parte do arquivo de texto da primeira ocorrência de uma string até a primeira ocorrência de outra

Como posso extrair uma parte de um arquivo de texto grande, começando na primeira ocorrência de FOO e terminando na primeira ocorrência de BAR?

No meu caso, estou tentando extrair uma parte de um arquivo sql criado pelo mysqldump.

Responder1

Créditos para@dgige@Pauloque me ajudaram com seus comentários!Final perluma linha aqui:

perl -lne 'if(/FOO/../BAR/){s/.*?(FOO)/$1/ if!$i++;s/BAR\K.*//&&print&&exit;print}' file

Explicação:

if(/FOO/../BAR/){        # perform the following actions on each line, starting
                         # with a line that contains FOO, and up to and including
                         # a line that contains BAR  
s/.*?(FOO)/$1/ if!$i++;  # only on the first line that contains FOO,
                         # delete all characters before FOO  
s/BAR\K.*//&&print&&exit;# if the line contains BAR, remove characters
                         # after BAR, print the line and stop processing  
print                    # simply print the line contents

Resposta antiga:

Créditos para@Paulopara uma sedsolução simples. É tão simples e fácil de ler awk:

awk '/FOO/,/BAR/' file

Poderia ser muito simples: retorna linhas inteiras e não exatamente "uma parte do texto começando na primeira ocorrência de FOO e terminando na primeira ocorrência de BAR". Acredito que isso significa que FOO deve ser a primeira palavra e BAR a última. Fazer exatamente isso requer uma resposta mais complicada. Deixe-me tentar fazer isso em perl.

Caso simples (retorna linhas inteiras):

perl -lne 'print if /FOO/../BAR/' file

Caso complexo (exatamente de FOO para BAR):

perl -lne 'if(/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if!$i++;$_=~s/BAR\K.*//;print}' file

Gosto desta solução equivalente, que atribui uma variável ao operador range:

perl -lne 'if($a=/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if$a==1;$_=~s/BAR\K.*// if$a=~/E/;print}' file

Observação:Supõe-se que haja apenas uma parte do texto a ser extraída, ou seja, não devemos encontrar outro FOO após o primeiro parágrafo delimitado por FOO e BAR.

Caso contrário, o caso simples já não é mais tão simples em awk:

awk '/FOO/,/BAR/ {print; if ($0~/BAR/) {exit} }' file

e em perl:

perl -lne '(print&&/BAR/&&exit) if /FOO/../BAR/' file

E as soluções complexas e mais refinadas tornam-se:

perl -lne 'if(/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if!$i++;$_=~s/BAR\K.*//&&print&&exit;print}' file

e:

perl -lne 'if($a=/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if$a==1;$_=~s/BAR\K.*//&&print&&exit if$a=~/E/;print}' file

Este exemplo mostra como uma linha única pode deixar de ser excepcionalmente clara e autoexplicativa para o que parece ser uma sequência obscura de caracteres aleatórios, por ter adicionado um pouco mais de complexidade ao problema. Sempre que necessário, eu recomendaria escrever um script independente, de fácil manutenção e legível, onde recursos extras possam ser facilmente adicionados e casos extremos levados em consideração.

Responder2

Neste caso não foi tão difícil como pensei que poderia ser. Com sed, desde a primeira ocorrência de FOO até a primeira ocorrência de BAR (não tentei, mas provavelmente algo como o segundo FOO até o segundo BAR seria mais difícil).

sed -nr '/FOO/ {
/FOO/ s/[^F]+FOO/FOO/p
:a
n
/BAR/ s/([^B]+BAR).*/\1/
p
/BAR/ q
ba
}' <<<'line1
> line2 FOO text1 FOO text2
> line3
> line4 BAR text3 BAR text4
> line5'

FOO text1 FOO text2
line3
line4 BAR

informação relacionada