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 modificadogrep
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
. Usegrep -r
com o GNU mais recentegrep
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. GNUgrep
ao contrário de muitos outrosgrep
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 a2.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 GNUgrep
2.6
Comentários
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
exargs -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
grep
pode descartar os buffers que processou até agora. Você podegrep
a saída deyes
indefinidamente sem usar mais do que alguns kilobytes de memória. O problema é o tamanho das linhas.--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.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