Grep em milhares de arquivos

13

Eu tenho diretório com cca 26 000 arquivos e eu preciso grep em todos esses arquivos. O problema é que eu preciso dele o mais rápido possível, por isso não é ideal criar scripts em que o grep pegue o nome de um arquivo no comando find e grave as correspondências no arquivo. Antes da edição da "lista de argumentos muito longa", demorava cerca de 2 minutos para grep em todos esses arquivos. Alguma ideia de como fazer? edit: existe um script que cria novos arquivos o tempo todo, portanto, não é possível colocar todos os arquivos em diretórios diferentes.

user2778979
fonte
1
use findwith xargsorgrep -R
Eddy_Em 7/13
Ele funciona bem, mas leva 10 minutos ...
user2778979

Respostas:

19

Com find:

cd /the/dir
find . -type f -exec grep pattern {} +

( -type fé apenas para pesquisar em arquivos regulares (também excluindo links simbólicos, mesmo que apontem para arquivos regulares). Se você deseja pesquisar em qualquer tipo de arquivo, exceto diretórios (mas tenha cuidado, existem alguns tipos de arquivos como fifos ou / dev / zero que você geralmente não quer ler), substitua -type fpelo específico do GNU ! -xtype d( -xtype dcorresponde a arquivos do diretório de tipos após a resolução do link simbólico)).

Com o GNU grep:

grep -r pattern /the/dir

(mas lembre-se de que, a menos que você tenha uma versão recente do GNU grep, seguirá links simbólicos ao descer para diretórios). Arquivos não regulares não serão pesquisados, a menos que você adicione uma -D readopção. grepPorém, versões recentes do GNU ainda não pesquisam dentro de links simbólicos.

Versões muito antigas do GNU findnão suportavam a {} +sintaxe padrão , mas era possível usar o não-padrão:

cd /the/dir &&
  find . -type f -print0 | xargs -r0 grep pattern

É provável que as performances sejam vinculadas à E / S. Esse é o momento de fazer a pesquisa e o tempo necessário para ler todos os dados do armazenamento.

Se os dados estiverem em uma matriz de disco redundante, a leitura de vários arquivos por vez poderá melhorar o desempenho (e degradá-los de outra forma). Se os desempenhos não estiverem vinculados à E / S (porque, por exemplo, todos os dados estão em cache) e você tiver várias CPUs, o simultâneo também grepspoderá ajudar. Você pode fazer isso com xargsa -Popção do GNU .

Por exemplo, se os dados estiverem em uma matriz RAID1 com 3 unidades ou se os dados estiverem em cache e você tiver 3 CPUs cujo tempo de sobra:

cd /the/dir &&
  find . -type f -print0 | xargs -n1000 -r0P3 grep pattern

(aqui usando -n1000para gerar um novo a grepcada 1000 arquivos, até 3 sendo executados em paralelo por vez).

No entanto, observe que, se a saída de grepfor redirecionada, você terminará com uma saída mal intercalada dos 3 grepprocessos; nesse caso, convém executá-la como:

find . -type f -print0 | stdbuf -oL xargs -n1000 -r0P3 grep pattern

(em um sistema recente GNU ou FreeBSD) ou use a --line-bufferedopção GNU grep.

Se patternfor uma sequência fixa, adicionar a -Fopção pode melhorar o assunto.

Se não forem dados de caracteres de vários bytes ou se for para a correspondência desse padrão, não importa se os dados são de caracteres de vários bytes ou não, então:

cd /the/dir &&
  LC_ALL=C grep -r pattern .

poderia melhorar significativamente o desempenho.

Se você acaba fazendo essas pesquisas com frequência, convém indexar seus dados usando um dos muitos mecanismos de pesquisa existentes.

Stéphane Chazelas
fonte
3

26000 arquivos em um único diretório é muito para a maioria dos sistemas de arquivos. É provável que uma parte significativa do tempo seja gasta na leitura desse grande diretório. Considere dividi-lo em diretórios menores, com apenas algumas centenas de arquivos cada.

As chamadas findnão podem explicar um desempenho ruim, a menos que você faça errado. É uma maneira rápida de percorrer um diretório e garantir que você não corra o risco de executar uma linha de comando muito longa. Certifique-se de usar o -exec grep PATTERN {} +que empacota o máximo de arquivos possível por chamada de comando e não o -exec grep PATTERN {} \;que executa grepuma vez por arquivo: a execução do comando uma vez por arquivo provavelmente será significativamente mais lenta.

Gilles 'SO- parar de ser mau'
fonte
Obrigado, vou pesquisar algo no Google e provavelmente vou dividir isso. Fiz exatamente o que você está escrevendo sobre e levou 3 vezes mais do que única grep ...
user2778979
Gilles, você está dizendo que o desempenho seria significativamente diferente para 26.000 arquivos em um diretório versus 26.000 arquivos distribuídos em, digamos, 100 diretórios?
User001
1
@ user001 Sim. A diferença entre eles depende do sistema de arquivos e possivelmente do armazenamento subjacente, mas eu esperaria que qualquer sistema de arquivos fosse mensurável mais rápido, com 260 arquivos em cada um dos 100 diretórios, em comparação com 26000 arquivos em um único diretório.
Gilles 'SO- stop be evil'
Obrigado pelo esclarecimento. Perguntei a um follow-up questão quanto a este ponto, a fim de compreender a base para a discrepância.
User001
0

Se você precisar grep TODOS os arquivos várias vezes (como você disse, executando um script), sugiro procurar em discos ram, copie todos os arquivos lá e depois grep os arquivos várias vezes, isso acelerará sua pesquisa por um fator de: pelo menos 100x.

Você só precisa de RAM suficiente. Caso contrário, você deve procurar a indexação dos arquivos, por exemplo. no lucene ou em um banco de dados nosql e executando consultas sobre isso.

Tobias Feldballe
fonte
Como observado em outro lugar, isso não ajuda no fato de haver muitos arquivos para executar grep. Há também o argumento de que: "existe um script que cria novos arquivos o tempo todo, portanto, não é possível colocar todos os arquivos em diretórios diferentes".
Jeff Schaller
-2

Todos os arquivos no diretório

grep 'search string' *

com recursivamente

grep -R 'search string' *
Markus
fonte
Cuidado para elaborar o -1?
Markus
4
Não diminuí a votação, mas há alguns problemas com o seu: o OP mencionou uma "lista de argumentos muito longa", que o seu primeiro não corrige e provavelmente é o que o OP estava fazendo antes. O segundo também não ajuda nesse sentido (teria ajudado se você tivesse usado em .vez de *). *excluirá arquivos de ponto (embora com -R, não nos diretórios recorrentes). -R ao contrário de -r segue links simbólicos, mesmo com versões recentes do GNU grep. Você também terá um problema com os arquivos no diretório atual cujo nome começa com-
Stéphane Chazelas