Extrai parte do arquivo de texto da primeira ocorrência de uma string para a primeira ocorrência de outra

1

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.

    
por dgig 10.04.2018 / 16:50

2 respostas

2

Créditos para @dgig e @Paulo que me ajudou com o feedback deles! Final perl one-liner 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 @Paulo para uma simples solução sed . É tão simples e fácil de ler em awk :

awk '/FOO/,/BAR/' file

Pode ser muito simples: retorna linhas inteiras e não exatamente "uma parte do texto que começa na primeira ocorrência de FOO e termina na primeira ocorrência de BAR". Eu acredito que isso signifique que FOO deveria ser a primeira palavra, e BAR a última. Fazer exatamente isso requer uma resposta mais complicada. Deixe-me tentar realizar isso em perl .

Caso simples (retorna linhas inteiras):

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

Caso complexo (exatamente de FOO a BAR):

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

Eu gosto dessa solução equivalente, que atribui uma variável ao operador de intervalo:

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

Nota: Assume-se que há apenas uma parte do texto a ser extraído, 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 se tornam:

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 um one-liner pode passar de excepcionalmente claro e auto-explicativo 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, recomendo escrever um script autônomo, sustentável e legível, no qual os recursos extras possam ser facilmente adicionados e os casos de canto levados em consideração.

    
por 12.04.2018 / 17:30
2

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

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

FOO text1 FOO text2
line3
line4 BAR
    
por 12.04.2018 / 21:16