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 cela?

Jai 10 Go de RAM sur mon système et quelques applications en cours dexécution, donc je suis vraiment surpris quun simple grep manque de mémoire. ~/Documents fait environ 100 Go et contient toutes sortes de fichiers.

grep -RI peut ne pas avoir ce problème, mais je veux pour rechercher aussi dans les fichiers binaires.

Réponse

Deux problèmes potentiels:

  • grep -R (sauf pour le GNU modifié grep trouvé sur OS / X 10.8 et plus) suit les liens symboliques, donc même sil ny a que 100 Go de fichiers dans ~/Documents, il peut encore y avoir un lien symbolique vers / par exemple et vous « finirez par analyser lensemble du système de fichiers, y compris des fichiers comme /dev/zero. Utilisez grep -r avec le nouveau GNU grep, ou utilisez la syntaxe standard:

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

    (notez cependant que létat de sortie ne reflète pas le fait que le modèle correspond ou non).

  • grep trouve les lignes qui correspondent au modèle. Pour cela, il doit charger une ligne à la fois en mémoire. GNU grep par opposition à beaucoup dautres grep implémentations na pas de limite sur la taille des lignes quil lit et supporte la recherche dans les fichiers binaires. Donc, si vous avez un fichier avec une très grande ligne (cest-à-dire avec deux caractères de nouvelle ligne très éloignés), plus grand que la mémoire disponible, il échouera.

    Cela se produirait généralement avec un fichier fragmenté. Vous pouvez le reproduire avec:

    truncate -s200G some-file grep foo some-file 

    Celui-ci 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 dalimenter lentrée à grep. Cela couvrirait les cas où le problème est dû à des fichiers épars.

    Vous pouvez loptimiser 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 ne sont pas clairsemés et que vous avez une version de GNU grep antérieure à 2.6, vous pouvez utiliser loption --mmap. Les lignes seront mmappées en mémoire au lieu dy être copiées, ce qui signifie que le système peut toujours récupérer le mémor y en paginant les pages vers le fichier. Cette option a été supprimée dans GNU grep 2.6

Commentaires

  • @GodricSeer, il peut toujours lire une grande partie du fichier dans un seul tampon, mais sil na ‘ t trouver la chaîne et na pas ‘ t a trouvé un caractère de nouvelle ligne non plus, mon pari est quil garde ce tampon unique en mémoire et lit le tampon suivant, car il devra lafficher si une correspondance est trouvée. Donc, le problème est toujours le même. En pratique, un grep sur un fichier fragmenté de 200 Go échoue avec MOO.
  • @GodricSeer, eh bien non. Si les lignes sont toutes petites, grep peut supprimer les tampons quil a traités jusquà présent. Vous pouvez grep la sortie de yes indéfiniment sans utiliser plus de quelques kilo-octets de mémoire. Le problème est la taille des lignes.
  • Loption GNU grep --null-data peut également être utile ici. Cela force lutilisation de NUL au lieu de la nouvelle ligne comme terminateur de ligne dentrée.
  • @ 1_CR, bon point, bien que cela définisse également le terminateur de ligne de sortie sur NUL.
  • Le fold aide de la commande dans ces situations? Par exemple, pensez à dd if=/dev/sda | fold -b $((4096*1024*1024)) | grep -a "some string" pour limiter la quantité de mémoire requise à 4 Go

Réponse

Je le fais habituellement

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

Jai essayé un tas de méthodes et jai trouvé que cétait la plus rapide. Notez que cela ne gère pas très bien les fichiers avec des espaces le nom de fichier. Si vous savez que cest 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 fichier.

Commentaires

  • Cela cassera sur les fichiers avec des espaces.
  • Hmm, cest vrai.
  • Vous pouvez contourner cela avec find -print0 | xargs -0 grep -ne 'expression'
  • @ChrisDown plutôt une solution non protable quune solution portable défectueuse.
  • @ChrisDown Most les unités principales ont adopté find -print0 et xargs -0 maintenant: les trois BSD, MINIX 3, Solaris 11,…

Réponse

Je peux penser à plusieurs façons de contourner ce problème:

  • À la place de grepping 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 à la place. Puisque grep arrêtera de chercher après le premier appel, il naura pas à continuer à lire des fichiers volumineux.

  • Si vous voulez aussi le texte réel, vous pouvez saisir deux séparer les greps le long de:

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

Commentaires

  • Le dernier exemple nest pas une syntaxe valide – vous ‘ devez effectuer une substitution de commande (et vous ne devriez pas ‘ faire cela, puisque grep utilise un délimiteur légal dans les noms de fichiers). Vous devez également citer $file.
  • Ce dernier exemple en souffre avec le problème des noms de fichiers contenant une nouvelle ligne ou un espace blanc (cela entraînera for de traiter le fichier comme deux arguments)
  • @DravSloan Votre modification, tandis une amélioration, des pauses toujours sur les noms de fichiers légaux.
  • Oui, je lai laissé parce que cela faisait partie de sa réponse, jai juste essayé de laméliorer pour quil fonctionne (pour les cas où s aucun espace / nouvelle ligne, etc. dans les fichiers).
  • Corrections de sa – > elle, mes excuses Jenny: /

Réponse

Je suis en train de récupérer un disque de 6 To pour rechercher des données perdues, et jai épuisé la mémoire -error. Cela devrait également fonctionner pour dautres fichiers.

La solution que nous avons trouvée était de lire le disque en morceaux en utilisant dd, et en grepping 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 

Commentaires

  • Sauf si vous lisez morceaux qui se chevauchent , vous risquez de manquer des correspondances sur les limites des morceaux. Le chevauchement doit être au moins aussi grand que la chaîne que vous espérez correspondre.
  • Mise à jour pour rechercher 1 Mo supplémentaire dans chaque bloc de 100 Mo … piratage bon marché

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *