Eu estava fazendo uma pesquisa muito simples:

grep -R Milledgeville ~/Documents 

E depois de algum tempo, este erro apareceu:

grep: memory exhausted 

Como posso evitar isso?

Tenho 10 GB de RAM em meu sistema e alguns aplicativos em execução, então estou realmente surpreso que um grep simples 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 eu quero para pesquisar em arquivos binários também.

Resposta

Dois problemas potenciais:

  • grep -R (exceto para o GNU modificado grep encontrado no OS / X 10.8 e superior) segue os links simbólicos, então mesmo se houver apenas 100 GB de arquivos em ~/Documents, ainda pode haver um link simbólico para /, por exemplo, e você “acabará examinando todo o sistema de arquivos, incluindo arquivos como /dev/zero. Use grep -r com o GNU mais recente 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 refletirá o fato de o padrão corresponder ou não).

  • grep encontra as linhas que correspondem ao padrão. Para isso, é necessário carregar uma linha por vez na memória. GNU grep ao contrário de muitos outros grep as implementações não têm um limite no tamanho das linhas que lê e oferecem suporte à 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 distantes), maior do que a memória disponível, ele falhará.

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

    truncate -s200G some-file grep foo some-file 

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

    find ~/Documents -type f -exec sh -c "for i do tr -s "\0" "\n" < "$i" | grep --label="$i" -He "$0" done" Milledgeville {} + 

    Isso converte sequê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ê poderia otimizá-lo fazendo isso apenas para arquivos grandes:

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

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

Comentários

  • @GodricSeer, ele ainda pode ler uma grande parte do arquivo em um único buffer, mas se não ‘ encontrar a string lá e não ‘ Também não encontrei um caractere de nova linha, minha aposta é que ele mantém aquele único buffer na memória e lê o próximo buffer, pois terá que exibi-lo se uma correspondência for encontrada. Então, o problema ainda é o mesmo. Na prática, um grep em um arquivo esparso de 200 GB falha com OOM.
  • @GodricSeer, bem, não. Se as linhas forem todas pequenas, grep pode descartar os buffers que processou até agora. Você pode grep a saída de yes indefinidamente sem usar mais do que alguns kilobytes de memória. O problema é o tamanho das linhas.
  • A opção GNU grep --null-data também pode ser útil aqui. Ele força o uso de NUL em vez de nova linha como um terminador de linha de entrada.
  • @ 1_CR, bom ponto, embora isso também defina o terminador de linha de saída para NUL.
  • O fold ajuda do comando nessas situações? Por exemplo, pense em dd if=/dev/sda | fold -b $((4096*1024*1024)) | grep -a "some string" para limitar a quantidade de memória necessária a 4 GB

Resposta

Costumo fazer

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

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

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

Caso contrário, você pode usar:

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

que exec um grep para cada arquivo.

Comentários

  • Isso vai quebrar em arquivos com espaços.
  • Hmm, isso é verdade.
  • Você pode contornar isso com find -print0 | xargs -0 grep -ne 'expression'
  • @ChrisDown em vez de uma solução não protegível do que uma solução portátil quebrada.
  • @ChrisDown Mais os principais unices adotaram find -print0 e xargs -0 até agora: todos os três BSD, MINIX 3, Solaris 11,…

Resposta

Posso pensar em algumas maneiras de contornar isso:

  • Em vez disso de executar o grep em todos os arquivos de uma vez, faça um arquivo por vez.Exemplo:

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

  • Se você também quiser o texto real, você pode string dois separe os greps ao longo:

    for file in $( grep -Rl Milledgeville /Documents ); do grep -H Milledgeville "$file"; done 

Comentários

  • O último exemplo não é uma sintaxe válida – você ‘ d precisa realizar uma substituição de comando (e você não deve ‘ fazer isso, pois grep resultados usando um delimitador válido em nomes de arquivo). Você também precisa citar $file.
  • O último exemplo sofre com o problema de nomes de arquivo com nova linha ou espaço em branco (fará com que for processe o arquivo como dois argumentos)
  • @DravSloan Sua edição, enquanto uma melhoria, ainda quebra em nomes de arquivos legais.
  • Sim, eu deixei porque fazia parte da resposta dela, eu só tentei melhorar para que funcionasse (para os casos em que eu s sem espaços / novas linhas etc nos arquivos).
  • Correções dele – > ela, minhas desculpas Jenny: /

Resposta

Estou executando um grep em um disco de 6 TB para pesquisar dados perdidos e esgotou a memória -error. Isso deve funcionar para outros arquivos também.

A solução que encontramos foi ler o disco em partes usando dd e grepping nas partes. Este é o código (big-grep.sh):

#problem: grep gives "memory exhausted" error on 6TB disks #solution: read it on parts if [ -z $2 ] || ! [ -e $1 ]; then echo "$0 file string|less -S # greps in chunks"; exit; fi FILE="$1" MATCH="$2" SIZE=`ls -l $1|cut -d\ -f5` CHUNKSIZE=$(( 1024 * 1024 * 1 )) CHUNKS=100 # greps in (100 + 1) x 1MB = 101MB chunks COUNT=$(( $SIZE / $CHUNKSIZE * CHUNKS )) for I in `seq 0 $COUNT`; do dd bs=$CHUNKSIZE skip=$(($I*$CHUNKS)) count=$(( $CHUNKS+1)) if=$FILE status=none|grep -UF -a --context 6 "$MATCH" done 

Comentários

  • A menos que você leia blocos sobrepostos , você possivelmente perderia correspondências nos limites dos blocos. A sobreposição deve ser pelo menos tão grande quanto a string que você espera encontrar.
  • Atualizado para pesquisar 1 MB extra em cada pedaço de 100 MB … hack barato

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *