grep para retornar as linhas N e M antes e depois da partida

12

Eu sei que com grep eu posso usar os campos -Ae -Bpuxar as linhas anteriores e seguintes de uma partida.

No entanto, eles puxam todas as linhas entre a correspondência com base no número de linhas especificadas.

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

Eu gostaria de receber apenas a 5 ª linha antes de um jogo ea 5 ª linha após a partida em adição à linha combinado e não obter as linhas entre.

Existe uma maneira de fazer isso com o grep?

Chollida
fonte
1
Você poderia fazer isso colocando-o no sed. Acabei de testar isso e funcionou, mas só funcionou quando houve 1 correspondência exata no arquivo: grep -r -i -B 5 -A 5 "match" | sed -e 1b -e '$!d'
Terrance
@Terrance, obrigado pela sugestão, como você mencionou, já que estou coletando milhares de linhas, isso não funcionará.
Chollida # 10/18
Eu não acho que grep vai funcionar por si só ... Eu estou trabalhando em um script bash para você
Joshua Besneatte
Sem problemas! Meio interessado em ver quais respostas você recebe. =)
Terrance
isso está em um arquivo ou em vários arquivos?
Joshua Besneatte

Respostas:

1

A ferramenta que você deseja usar é chamada peneirar. Este é basicamente um grep com esteróides. Grep em paralelo. O Sift tem uma enorme quantidade de opções para fazer exatamente o que você deseja - especificamente para retornar uma linha específica em relação a uma (s) correspondência (s) que pode / não pode ser seguida / precedida por algum texto.

Surpreende-me que o sift não seja o gnu convencional, pois foi escrito na linguagem go, mas é instalado no Linux muito bem. A TI pesquisa em paralelo usando todas as enormes quantidades de texto de cpus, onde o grep leva apenas algumas semanas para fazer o mesmo.

Peneire o site - veja exemplos

Brandon Haberfeld
fonte
Bem-vindo ao AskUbuntu, obrigado por responder. Você precisa fornecer um exemplo de CLI que possa resolver esse problema específico, em vez de fornecer um link para o site da peneira. Este é um resumo de perguntas e respostas, obrigado.
Bernard Wei
12

E 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
Glenn Jackman
fonte
+1, mas você poderia explicar a semântica de /match/ {matched[NR]}? Eu nunca vi uma matriz ou variável como um comando inteiro. É colocar o número do registro atual de cada linha correspondente na matriz.
21418 Joe
Essa é uma singularidade estranha: se você fizer referência a um elemento da matriz sem atribuição, essa chave será adicionada à matriz (sem um valor). Em seguida, essa chave aparece na expressão key in array. O que estou fazendo é lembrar os números de linha onde o padrão aparece
Glenn Jackman
6

Esta é basicamente a solução de Glenn, mas implementada com Bash, Grep e 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 linhas menores que 1 cometerão erros sed e números de linhas maiores que o número de linhas no arquivo não imprimirão nada.

Este é apenas o mínimo. Para fazê-lo funcionar recursivamente e lidar com o número de linhas acima, levaria algum tempo.

wjandrea
fonte
6

Não pode ser feito apenas grep. Se for eduma 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.

JoL
fonte
5
@ubashu Você acha que será mais útil para o OP dar um plano simples "não pode ser feito com grep"? Estou fornecendo o que acredito ser uma boa alternativa para resolver o problema do OP. Na Central de Ajuda: "Qual é a pergunta específica? Verifique se sua resposta fornece isso - ou uma alternativa viável. A resposta pode ser 'não faça isso', mas também deve incluir 'tente fazer isso' . "
JOL
edé sempre uma resposta, porque edé o editor de texto padrão.
dessert
5
@ubashu Embora não seja uma grepresposta, a resposta "Você não pode fazer isso com X, mas com Y, veja como" ainda é uma resposta válida, pois você não apenas responde à pergunta da OP, mas também fornece uma alternativa Isso funcionaria. Este é um tipo válido de resposta aqui.
Thomas Ward
5
awk '/match/{system("sed -n \"" NR-5 "p;" NR "p;" NR+5 "p\" " FILENAME)}' infile

Aqui estamos usando a função do awk para chamar o comando externo para imprimir as linhas que o awk combinou com o padrão com a quinta linha antes e depois da partida.system(command)sedmatch

A sintaxe é fácil, você só precisa colocar o comando externo entre aspas duplas, assim como suas opções, e escapar do que deseja passar exatamente para o comando; tudo o mais relacionado às awkopções em si deve estar fora das aspas. Então, o abaixo sed :

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

traduzir para:

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

NRé o número da linha que corresponde ao padrão matche FILENAMEé o nome do arquivo de processamento atual que está passando awk.

αғsнιη
fonte
2

usando o arquivo de texto de exemplo de @ glenn e usando perl em vez de awk:

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

dará os mesmos resultados, mas rodando mais rápido:

a
f match
k
d
i match
n
Fabby
fonte
João, você está aparecendo na fila de revisão do LQ e o @waltinator votou para excluir, portanto, da próxima vez, seja um pouco mais detalhado ... ;-) Também marque +1 para tirá-lo da fila do LQ ... : P
Fabby
1
@JJoao Fila de avaliações de baixa qualidade. Sua resposta provavelmente foi escolhida por ser um código de 90%.
Wjandrea
1
@JJoao A cifra de 90% é apenas minha maneira de explicar. Não sei quais heurísticas são realmente usadas.
Wjandrea
1
Menos café, mais escrita! @JJoao : D ;-): D
Fabby
1
@Fabby: Sem nada café Funciona: D - provavelmente que iria aparecer na LCQ (= baixo fila de café)