Grep de muitas palavras-chave em muitos arquivos: acelerando

4

Atualmente estou enfrentando um "problema de desempenho" ao usar o grep. Eu estou tentando localizar as ocorrências de muitas (mais de 10.000) palavras-chave em muitos arquivos (pense tamanho do repositório do kernel Linux). O objetivo é gerar um tipo de índice para cada palavra-chave:

keyword: my_keyword
filepath 1
filepath 2
------
keyword: my_keyword2
...

Sendo relativamente novo no shell de scripts, tentei algumas abordagens ingênuas:

while read LINE; do
    echo "keyword: "$LINE >> $OUTPUT_FILE
    grep -E -r --include='Makefile*' "($LINE)" . >> "$OUTPUT_FILE"
    echo "-----"
done < $KEYWORD_FILE

Isso leva cerca de 45 minutos para ser concluído, com os resultados esperados.

Nota: todas as palavras-chave que eu procuro estão localizadas em um arquivo ("KEYWORD_FILE", portanto, corrigidas antes de iniciar qualquer coisa) e os arquivos nos quais pesquisar podem ser determinados antes que a pesquisa seja iniciada. Tentei armazenar a lista de arquivos para pesquisar antes da pesquisa assim:

file_set=( $(find . -name "Kbuild*" -o -name "Makefile*") )

e, em seguida, substitua a chamada grep por

echo {$file_set[*]} | parallel -k -j+0 -n 1000 -m grep -H -l -w "\($LINE\)" >> "$OUTPUT_FILE"

Demora cerca de uma hora para concluir…

Pergunta: considerando que eu posso usar qualquer técnica que eu queira, desde que seja compatível, como isso pode ser feito "mais rápido" e / ou mais eficiente?

Talvez grep não seja a ferramenta a ser usada, e meu uso de parallel está errado ... todas as ideias são bem-vindas!

    
por Nico 27.03.2013 / 10:49

4 respostas

6

The objective is to generate a kind of index for each keyword:

Em seguida, use o software de indexação em vez do grep. Tive muito sucesso ao usar pesquisa de códigos em todo o Arquivo CPAN .

    
por 27.03.2013 / 12:35
1

Eu escreveria um script em Perl (sim, eu sei que o Python é atualmente muito mais favorecido ...) que faz slurps nas 10.000 palavras-chave, lê cada arquivo e para cada linha vê se algumas delas correspondem. Se houver uma correspondência, armazene-a sob a palavra-chave como arquivo / linha (um hash funciona bem aqui). Uma vez feito com o conjunto, passe o hash cuspindo as descobertas para cada palavra-chave.

Perl (e Python) é uma linguagem (semi) compilada, o script é compilado em uma representação interna (bastante compacta, fácil e rápida de interpretar), e essa forma interna recebe (alguns) "otimizadores" de amor. A velocidade não é a mesma do C, mas não deve ser 10 vezes mais lenta.

No final, o seu comentário acima acerta na cabeça: Seu tempo (escrever, depurar ou buscar, construir, aprender a usar) é muito mais valioso do que o tempo da máquina (se você deixá-lo funcionando durante a noite, quem se importa se for feito por dez horas ou seis horas ...).

    
por 27.03.2013 / 15:18
0

Usar os switches -f e -F no grep provavelmente acelerará um pouco as coisas. O Grep pode ser bastante inteligente sobre como lidar com correspondências para múltiplos padrões.

The objective is to generate a kind of index for each keyword

Então, talvez a solução seja um mecanismo de pesquisa (por exemplo, mnogo)?

    
por 27.03.2013 / 11:21
0

Estes pontos vêm à minha mente:

  1. Você não pode seriamente grep cada arquivo uma vez para cada palavra-chave ... E também sem -l / --files-with-matches , cobrindo inutilmente até o final de cada arquivo. Infelizmente não há (AFAIK) nenhuma combinação de --files-with-matches com vários padrões (pelo menos não da maneira que você precisa). Isso pode ser feito no awk. O awk não pode ser chamado recursivamente (AFAIK), mas pode ser chamado através de uma localização com muitos arquivos.
  2. Eu fiz a experiência de que as operações com acessos a muitos arquivos (ou melhor, seus inodes) podem ser aceleradas muito, percorrendo a árvore com o primeiro. Eu acho que isso reduz muito os movimentos da cabeça do disco, porque as entradas de diretório e os inodes são lidos em blocos e esses metadados são armazenados em cache. Não faça com que a impressão seja encontrada em todo o caminho, -printf . é suficiente e grave a saída em / dev / null.
  3. A solução mais rápida provavelmente não é confiar em ferramentas shell, mas escrever algo que é compilado (por exemplo, Python (edit :) com um JIT). Dessa forma, você pode otimizar tanto o I / O (implementando --files-with-matches) e CPU (apenas testar cada linha para as palavras-chave que ainda não corresponderam) e acrescentar o caminho do arquivo para cada arquivo de índice simultaneamente (para tmpfs? ); mantenha um FD aberto para cada palavra-chave (dado que você tem menos de 1000). Depois disso, você só precisa concatenar esses arquivos. Até mesmo o cache de localização pode ser otimizado desta maneira: Primeiro, leia os nomes dos arquivos até que você tenha, por exemplo. 1000 correspondentes, em seguida, ler seus inodes (stat () o arquivo), em seguida, pesquisar os arquivos, em seguida, leia os próximos 1000 nomes.
por 27.03.2013 / 12:46