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 perl
uma 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 sed
soluçã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