grep para retornar as linhas Nth e Mth antes e depois da partida

11

Eu sei que com o grep eu posso usar os campos -A e -B para puxar as linhas anterior e seguinte de uma correspondência.

No entanto, eles colocam todas as linhas entre as partidas com base em quantas linhas forem especificadas.

grep -r -i -B 5 -A 5 "match" 

Gostaria de receber apenas a linha 5 th antes de uma partida e a linha 5 th após a partida além da linha correspondente e não obter as linhas entre.

Existe uma maneira de fazer isso com o grep ?

    
por chollida 10.05.2018 / 18:20

5 respostas

13

Se:

cat file
a
b
c
d
e
f match
g
h
i match
j
k
l
m
n
o

Então:

awk '
    {line[NR] = $0} 
    /match/ {matched[NR]} 
    END {
        for (nr in matched)
            for (n=nr-5; n<=nr+5; n+=5) 
                print line[n]
    }
' file
a
f match
k
d
i match
n
    
por glenn jackman 10.05.2018 / 18:36
7

Não pode ser feito com apenas grep . Se ed é uma opção:

ed -s file << 'EOF' 
g/match/-5p\
+5p\
+5p
EOF  

O script basicamente diz: para cada correspondência de / match /, imprima a linha 5 linhas antes disso, depois 5 linhas depois disso, depois 5 linhas depois disso.

    
por JoL 11.05.2018 / 01:25
6

Esta é basicamente a solução da Glenn, mas implementada com o Bash, o Grep e o sed.

grep -n match file |
    while IFS=: read nr _; do
        sed -ns "$((nr-5))p; $((nr))p; $((nr+5))p" file
    done

Observe que números de linha menores que 1 causam erro sed e números de linha maiores que o número de linhas no arquivo farão com que não seja impresso nada.

Este é apenas o mínimo. Para fazê-lo funcionar de forma recursiva e lidar com o número de linha acima, os casos levariam algum trabalho.

    
por wjandrea 10.05.2018 / 21:13
6
awk '/match/{system("sed -n \"" NR-5 "p;" NR "p;" NR+5 "p\" " FILENAME)}' infile

Aqui estamos usando a função system(command) do awk para chamar o comando sed para imprimir as linhas que awk combinaram com padrão match com 5 linhas th antes e depois da partida.

A sintaxe é fácil, você só precisa colocar o comando externo em si dentro de aspas duplas, assim como seus switches e escapar das coisas que você quer passar exatamente para o comando, tudo o mais relacionado às opções awk em si deve ser fora das cotações. Então o abaixo sed :

"sed -n \"" NR-5 "p;" NR "p;" NR+5 "p\" " FILENAME

traduza para:

sed -n "NR-5p; NRp; NR+5p" FILENAME

NR é o número da linha que corresponde ao padrão match e FILENAME é o processamento atual filename passando por awk .

    
por devWeek 10.05.2018 / 20:51
2

usando o arquivo de texto de exemplo do @glenn e usando perl ao invés de awk:

$ perl -n0E 'say /(.*\n)(?=(?:.*\n){4}(.*match.*\n)(?:.*\n){4}(.*\n))/g' ex

fornecerá os mesmos resultados, mas será mais rápido:

a
f match
k
d
i match
n
    
por JJoao 11.05.2018 / 12:18