grep: memory épuisée

Je faisais une search très simple:

grep -R Milledgeville ~/Documents 

Et après quelque time cette erreur est apparue:

 grep: memory exhausted 

Comment puis-je éviter cela?

J'ai 10 Go de RAM sur mon système et quelques applications en cours d'exécution, donc je suis vraiment surpris qu'un grep simple manque de memory. ~/Documents est d'environ 100 Go et contient toutes sortes de files.

grep -RI pourrait ne pas avoir ce problème, mais je veux aussi chercher dans des files binarys.

Deux problèmes potentiels:

  • grep -R (à l'exception du grep GNU modifié trouvé sur OS / X 10.8 et plus) suit les liens symboliques, donc même s'il n'y a que 100 Go de files dans ~/Documents , il peut y avoir un lien symbolique vers / vers le haut de l'set du système de files, y compris les files comme /dev/zero . Utilisez grep -r avec un grep GNU plus récent ou utilisez la syntaxe standard:

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

    (notez cependant que le statut de sortie ne reflétera pas le fait que le model est apparié ou non).

  • grep trouve les lignes qui correspondent au model. Pour cela, il doit charger une ligne à la fois en memory. GNU grep contrairement à beaucoup d'autres implémentations grep n'a pas de limite sur la taille des lignes qu'il lit et supporte la search dans les files binarys. Donc, si vous avez un file avec une très grande ligne (c'est-à-dire avec deux caractères de ligne très éloignée), plus grand que la memory disponible, il échouera.

    Cela se produit généralement avec un file clairsemé. Vous pouvez le reproduire avec:

     truncate -s200G some-file grep foo some-file 

    Celui-ci est difficile à contourner. Vous pourriez le faire comme (toujours avec GNU grep ):

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

    Cela convertit les séquences de caractères NUL en un caractère de nouvelle ligne avant d'alimenter l'input en grep . Cela couvrirait les cas où le problème est dû à des files épars.

    Vous pouvez l'optimiser en le faisant uniquement pour les files volumineux:

     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 {} + \) 

    Si les files ne sont pas clairsemés et que vous avez une version de GNU grep antérieure à 2.6 , vous pouvez utiliser l'option --mmap . Les lignes seront mmapped dans la memory par opposition à copyr là, ce qui signifie que le système peut toujours récupérer la memory en paginant les pages vers le file. Cette option a été supprimée dans GNU grep 2.6

Je peux penser à quelques façons de contourner ceci:

  • Au lieu de grepper tous les files à la fois, faites un file à la fois. Exemple:

     find /Documents -type f -exec grep -H Milledgeville "{}" \; 
  • Si vous avez seulement besoin de savoir quels files contiennent les mots, faites plutôt grep -l . Puisque grep s'arrêtera de chercher après le premier hit, il n'aura pas à lire d'énormes files

  • Si vous voulez également le text, vous pouvez enstringr deux files greps séparés:

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

Je fais habituellement

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

J'ai essayé un tas de methods, et trouvé que c'était le plus rapide. Notez que cela ne gère pas très bien les files avec des espaces. Si vous savez que c'est le cas et que vous avez une version GNU de grep, vous pouvez utiliser:

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

Sinon, vous pouvez utiliser:

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

Ce qui exec un grep pour chaque file.