grep: memória esgotada

38

Eu estava fazendo uma pesquisa muito simples:

grep -R Milledgeville ~/Documents

E depois de algum tempo, esse erro apareceu:

grep: memory exhausted

Como posso evitar isso?

Eu tenho 10GB de RAM no meu sistema e poucos aplicativos rodando, então estou realmente surpreso que um simples grep fique sem memória. ~/Documents tem cerca de 100 GB e contém todos os tipos de arquivos.

grep -RI pode não ter esse problema, mas também quero procurar em arquivos binários.

    
por Nicolas Raoul 10.09.2013 / 10:55

3 respostas

46

Dois problemas potenciais:

  • grep -R (exceto o GNU grep modificado encontrado no OS / X 10.8 e acima) segue links simbólicos, portanto, mesmo que haja apenas 100 GB de arquivos em ~/Documents , ainda pode haver um link simbólico para / Por exemplo, você acabará verificando todo o sistema de arquivos, incluindo arquivos como /dev/zero . Use grep -r com o mais novo GNU grep ou use a sintaxe padrão:

    find ~/Documents -type f -exec grep Milledgeville /dev/null {} +
    

    (no entanto, observe que o status de saída não reflete o fato de que o padrão é correspondido ou não).

  • grep localiza as linhas que correspondem ao padrão. Para isso, tem que carregar uma linha de cada vez na memória. O% GNUgrep, ao contrário de muitas outras implementações grep , não tem um limite no tamanho das linhas que lê e suporta a pesquisa em arquivos binários. Portanto, se você tiver um arquivo com uma linha muito grande (ou seja, com dois caracteres de nova linha muito mais próximos), maior que a memória disponível, ele falhará.

    Isso normalmente acontece com um arquivo esparso. Você pode reproduzi-lo com:

    truncate -s200G some-file
    grep foo some-file
    

    Essa é difícil de contornar. Você poderia fazer isso como (ainda com GNU grep ):

    find ~/Documents -type f -exec sh -c 'for i do
      tr -s "
    find ~/Documents -type f \( -size -100M -exec \
      grep -He Milledgeville {} + -o -exec sh -c 'for i do
      tr -s "
    find ~/Documents -type f -exec grep Milledgeville /dev/null {} +
    
    " "\n" < "$i" | grep --label="$i" -He "$0" done' Milledgeville {} + \)
    " "\n" < "$i" | grep --label="$i" -He "$0" done' Milledgeville {} +

    Isso converte seqüências de caracteres NUL em um caractere de nova linha antes de alimentar a entrada para grep . Isso cobriria os casos em que o problema é devido a arquivos esparsos.

    Você pode otimizá-lo fazendo isso apenas para arquivos grandes:

    truncate -s200G some-file
    grep foo some-file
    

    Se os arquivos não forem esparsos e você tiver uma versão do GNU grep anterior ao 2.6 , você pode usar a opção --mmap . As linhas serão mapeadas na memória em vez de copiadas, o que significa que o sistema pode sempre recuperar a memória paginando as páginas para o arquivo. Essa opção foi removida no GNU grep 2.6

por 10.09.2013 / 13:26
5

Eu costumo fazer

find ~/Documents | xargs grep -ne 'expression'

Eu tentei vários métodos e descobri que este é o mais rápido. Observe que isso não manipula arquivos com espaços, o nome do arquivo muito bem. Se você sabe que este é o caso e tem uma versão GNU do grep, você pode usar:

find ~/Documents -print0 | xargs -0 grep -ne 'expression'

Se não, você pode usar:

 find ~/Documents -exec grep -ne 'expression' "{}" \;

Qual será exec um grep para cada arquivo.

    
por 10.09.2013 / 12:46
4

Posso pensar em algumas maneiras de contornar isso:

  • Em vez de usar todos os arquivos ao mesmo tempo, faça um arquivo de cada vez. Exemplo:

    find /Documents -type f -exec grep -H Milledgeville "{}" \;
    
  • Se você precisa saber apenas quais arquivos contêm as palavras, faça grep -l . Como o grep irá parar de procurar após o primeiro hit, ele não terá que continuar lendo nenhum arquivo grande

  • Se você quiser também o texto atual, você pode digitar dois tipos separados:

    for file in $( grep -Rl Milledgeville /Documents ); do grep -H Milledgeville "$file"; done
    
por 10.09.2013 / 11:05