grep recursivo vs find / -tipo f -exec grep {} \; Qual é mais eficiente / mais rápido?

62

Qual é mais eficiente para descobrir quais arquivos em um sistema de arquivos inteiro contêm uma string: grep recursivo ou find grep em uma instrução exec? Eu assumo achado seria mais eficiente porque você pode pelo menos fazer alguma filtragem se você souber a extensão de arquivo ou um regex que coincide com o nome do arquivo, mas quando você só sabe -type f qual é melhor? GNU grep 2.6.3; find (GNU findutils) 4.4.2

Exemplo:

grep -r -i 'the brown dog' /

find / -type f -exec grep -i 'the brown dog' {} \;

    
por Gregg Leventhal 22.05.2014 / 16:53

4 respostas

73

Não tenho certeza:

grep -r -i 'the brown dog' /*

é realmente o que você quis dizer. Isso significaria grep recursivamente em todos os arquivos não ocultos e dirs em / (mas ainda olhe dentro dos arquivos ocultos e dirs dentro deles).

Supondo que você quis dizer:

grep -r -i 'the brown dog' /

Algumas coisas a serem observadas:

  • Nem todas as implementações grep suportam -r . E entre os que o fazem, os comportamentos diferem: alguns seguem links simbólicos para diretórios ao percorrer a árvore de diretórios (o que significa que você pode acabar procurando várias vezes no mesmo arquivo ou até executar em loops infinitos), outros não. Alguns vão olhar dentro dos arquivos do dispositivo (e vai demorar algum tempo em /dev/zero , por exemplo) ou canos ou arquivos binários ..., outros não.
  • É eficiente porque grep começa a procurar dentro dos arquivos assim que os descobre. Mas, enquanto olha em um arquivo, ele não está mais procurando por mais arquivos para pesquisar (o que provavelmente é melhor que na maioria dos casos)

Seu:

find / -type f -exec grep -i 'the brown dog' {} \;

(removeu o -r que não fazia sentido aqui) é terrivelmente ineficiente porque você está executando um grep por arquivo. ; só deve ser usado para comandos que aceitam apenas um argumento. Além disso, aqui, porque grep parece apenas em um arquivo, ele não imprimirá o nome do arquivo, portanto você não saberá onde estão as correspondências.

Você não está procurando dentro de arquivos de dispositivos, canais, links simbólicos ..., você não está seguindo links simbólicos, mas ainda está potencialmente procurando por coisas como /proc/mem .

find / -type f -exec grep -i 'the brown dog' {} +

seria muito melhor porque seriam executados o mínimo de grep de comandos possíveis. Você obteria o nome do arquivo, a menos que a última execução tivesse apenas um arquivo. Para isso, é melhor usar:

find / -type f -exec grep -i 'the brown dog' /dev/null {} +

ou com o GNU grep :

find / -type f -exec grep -Hi 'the brown dog' {} +

Observe que grep não será iniciado até que find encontre arquivos suficientes para serem mastigados, portanto haverá algum atraso inicial. E find não continuará procurando mais arquivos até que o grep anterior tenha retornado. Alocar e passar a lista de arquivos grandes tem algum (provavelmente insignificante) impacto, então, no geral, provavelmente será menos eficiente do que um grep -r que não segue o symlink ou olha dentro de dispositivos.

Com ferramentas GNU:

find / -type f -print0 | xargs -r0 grep -Hi 'the brown dog'

Como acima, o mínimo possível de grep instâncias será executado, mas find continuará procurando por mais arquivos enquanto a primeira grep invocação estiver procurando dentro do primeiro lote. Isso pode ou não ser uma vantagem. Por exemplo, com dados armazenados em discos rígidos rotativos, find e grep acessando dados armazenados em locais diferentes no disco diminuirão a taxa de transferência do disco fazendo com que a cabeça do disco se mova constantemente. Em uma configuração de RAID (onde find e grep podem acessar discos diferentes) ou em SSDs, isso pode fazer uma diferença positiva.

Em uma configuração de RAID, a execução de várias chamadas simultâneas grep também pode melhorar as coisas. Ainda com ferramentas GNU no armazenamento RAID1 com 3 discos,

find / -type f -print0 | xargs -r0 -P2 grep -Hi 'the brown dog'

pode aumentar significativamente o desempenho. No entanto, observe que o segundo grep só será iniciado quando forem encontrados arquivos suficientes para preencher o primeiro comando grep . Você pode adicionar uma opção -n a xargs para que isso aconteça mais cedo (e passar menos arquivos por grep invocação).

Observe também que, se você estiver redirecionando a saída xargs para qualquer coisa que não seja um dispositivo terminal, o greps s começará a armazenar em buffer sua saída, o que significa que a saída desses grep s provavelmente será intercalada incorretamente. Você teria que usar stdbuf -oL (onde disponível como no GNU ou FreeBSD) para contornar isso (você ainda pode ter problemas com linhas muito longas (geralmente > 4KiB)) ou ter cada um escrevendo sua saída em um separado arquivar e concatenar todos eles no final.

Aqui, a string que você está procurando é fixa (não um regexp), portanto, usar a opção -F pode fazer a diferença (improvável, pois grep implementations sabem como otimizar isso já).

Outra coisa que poderia fazer uma grande diferença é corrigir o código do idioma para C se você estiver em um código de idioma de vários bytes:

find / -type f -print0 | LC_ALL=C xargs -r0 -P2 grep -Hi 'the brown dog'

Para evitar olhar para dentro de /proc , /sys ..., use -xdev e especifique os sistemas de arquivos nos quais você deseja pesquisar:

LC_ALL=C find / /home -xdev -type f -exec grep -i 'the brown dog' /dev/null {} +

Ou limpe os caminhos que você deseja excluir explicitamente:

LC_ALL=C find / \( -path /dev -o -path /proc -o -path /sys \) -prune -o \
  -type f -exec grep -i 'the brown dog' /dev/null {} +
    
por 22.05.2014 / 21:37
12

Se a * na chamada grep não for importante para você, a primeira deve ser mais eficiente, pois apenas uma instância de grep é iniciada e os garfos não são gratuitos. Na maioria dos casos, será mais rápido até mesmo com o * , mas nos casos de borda a classificação pode reverter isso.

Pode haver outras estruturas find - grep que funcionam melhor, especialmente com muitos arquivos pequenos. Ler grandes quantidades de entradas de arquivos e inodes de uma só vez pode melhorar a performance na mídia rotativa.

Mas vamos dar uma olhada nas estatísticas do syscall:

encontre

> strace -cf find . -type f -exec grep -i -r 'the brown dog' {} \;
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 97.86    0.883000        3619       244           wait4
  0.53    0.004809           1      9318      4658 open
  0.46    0.004165           1      6875           mmap
  0.28    0.002555           3       977       732 execve
  0.19    0.001677           2       980       735 stat
  0.15    0.001366           1      1966           mprotect
  0.09    0.000837           0      1820           read
  0.09    0.000784           0      5647           close
  0.07    0.000604           0      5215           fstat
  0.06    0.000537           1       493           munmap
  0.05    0.000465           2       244           clone
  0.04    0.000356           1       245       245 access
  0.03    0.000287           2       134           newfstatat
  0.03    0.000235           1       312           openat
  0.02    0.000193           0       743           brk
  0.01    0.000082           0       245           arch_prctl
  0.01    0.000050           0       134           getdents
  0.00    0.000045           0       245           futex
  0.00    0.000041           0       491           rt_sigaction
  0.00    0.000041           0       246           getrlimit
  0.00    0.000040           0       489       244 ioctl
  0.00    0.000038           0       591           fcntl
  0.00    0.000028           0       204       188 lseek
  0.00    0.000024           0       489           set_robust_list
  0.00    0.000013           0       245           rt_sigprocmask
  0.00    0.000012           0       245           set_tid_address
  0.00    0.000000           0         1           uname
  0.00    0.000000           0       245           fchdir
  0.00    0.000000           0         2         1 statfs
------ ----------- ----------- --------- --------- ----------------
100.00    0.902284                 39085      6803 total

apenas grep

> strace -cf grep -r -i 'the brown dog' .
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 40.00    0.000304           2       134           getdents
 31.71    0.000241           0       533           read
 18.82    0.000143           0       319         6 openat
  4.08    0.000031           4         8           mprotect
  3.29    0.000025           0       199       193 lseek
  2.11    0.000016           0       401           close
  0.00    0.000000           0        38        19 open
  0.00    0.000000           0         6         3 stat
  0.00    0.000000           0       333           fstat
  0.00    0.000000           0        32           mmap
  0.00    0.000000           0         4           munmap
  0.00    0.000000           0         6           brk
  0.00    0.000000           0         2           rt_sigaction
  0.00    0.000000           0         1           rt_sigprocmask
  0.00    0.000000           0       245       244 ioctl
  0.00    0.000000           0         1         1 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0       471           fcntl
  0.00    0.000000           0         1           getrlimit
  0.00    0.000000           0         1           arch_prctl
  0.00    0.000000           0         1           futex
  0.00    0.000000           0         1           set_tid_address
  0.00    0.000000           0       132           newfstatat
  0.00    0.000000           0         1           set_robust_list
------ ----------- ----------- --------- --------- ----------------
100.00    0.000760                  2871       466 total
    
por 22.05.2014 / 17:56
5

Se você está em um SSD e o tempo de busca é insignificante, você pode usar o GNU paralelo:

find /path -type f | parallel --gnu --workdir "$PWD" -j 8 '
    grep -i -r 'the brown dog' {} 
'

Isso executará até 8 processos grep ao mesmo tempo, com base no que find encontrou.

Isto irá espancar um disco rígido, mas um SSD deve lidar bem com ele.

    
por 11.06.2015 / 20:29
-1

Mais uma coisa a considerar sobre isso é a seguinte.

Algum dos diretórios que o grep terá que percorrer recursivamente contém mais arquivos que a configuração do nofile do seu sistema? (por exemplo, o número de identificadores de arquivos abertos, o padrão é 1024 na maioria das distribuições linux)

Em caso afirmativo, encontrar é definitivamente o caminho a seguir, pois certas versões do grep serão eliminadas com um erro Lista de argumentos muito longa quando atinge um diretório com mais arquivos do que a configuração máxima de identificadores de arquivos abertos.

Apenas meu 2 ¢.

    
por 23.05.2014 / 18:38

Tags