web-dev-qa-db-fra.com

grep: mémoire épuisée

Je faisais une recherche très simple:

grep -R Milledgeville ~/Documents

Et après un certain temps, cette erreur est apparue:

grep: memory exhausted

Comment puis-je éviter ça?

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

grep -RI n'a peut-être pas ce problème, mais je souhaite également effectuer une recherche dans les fichiers binaires.

42
Nicolas Raoul

Deux problèmes potentiels:

  • grep -R (à l'exception de la modification GNU grep trouvée sur OS/X 10.8 et supérieur) suit les liens symboliques, donc même s'il n'y a que 100 Go de fichiers dans ~/Documents, il peut encore y avoir un lien symbolique vers / par exemple et vous finirez par analyser l'ensemble du système de fichiers, y compris des fichiers comme /dev/zero. Utilisation grep -r avec une version plus récente GNU grep, ou utilisez la syntaxe standard:

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

    (cependant, notez que le statut de sortie ne reflétera pas le fait que le modèle est ou non assorti).

  • grep trouve les lignes qui correspondent au motif. Pour cela, il doit charger une ligne à la fois en mémoire. GNU grep contrairement à beaucoup d'autres implémentations grep n'a pas de limite sur la taille des lignes qu'il lit et prend en charge la recherche dans les fichiers binaires. Donc , si vous avez un fichier avec une très grande ligne (c'est-à-dire avec deux caractères de nouvelle ligne très éloignés), plus grand que la mémoire disponible, il échouera.

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

    truncate -s200G some-file
    grep foo some-file
    

    Celui-là est difficile à contourner. Vous pouvez 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'entrée vers grep. Cela couvrirait les cas où le problème est dû à des fichiers rares.

    Vous pouvez l'optimiser en le faisant uniquement pour les fichiers 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 fichiers sont pas clairsemés et que vous avez une version de GNU grep avant 2.6, vous pouvez utiliser le --mmap option. Les lignes seront mappées en mémoire plutôt que copiées là-bas, ce qui signifie que le système peut toujours récupérer la mémoire en paginant les pages dans le fichier. Cette option a été supprimée dans GNU grep 2.6

46
Stéphane Chazelas

Je fais d'habitude

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

J'ai essayé un tas de méthodes et j'ai trouvé que c'était la plus rapide. Notez que cela ne gère pas très bien les fichiers avec des espaces dont le nom de fichier. 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 va exec un grep pour chaque fichier.

5
Kotte

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

  • Au lieu d'accueillir tous les fichiers à la fois, faites un fichier à la fois. Exemple:

    find /Documents -type f -exec grep -H Milledgeville "{}" \;
    
  • Si vous avez seulement besoin de savoir quels fichiers contiennent les mots, faites grep -l au lieu. Étant donné que grep cessera de rechercher après le premier coup, il ne devra pas continuer à lire les fichiers volumineux

  • Si vous voulez également le texte réel, vous pouvez enchaîner deux greps distincts:

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

J'embrasse un disque de 6 To pour rechercher des données perdues, et j'ai eu la mémoire épuisée - erreur. Cela devrait également fonctionner pour d'autres fichiers.

La solution que nous avons trouvée était de lire le disque en morceaux en utilisant dd, et en saluant les morceaux. Voici le code (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
3
PHZ.fi-Pharazon