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
. Utilisezgrep -r
avec le nouveau GNUgrep
, 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. GNUgrep
par opposition à beaucoup dautresgrep
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 GNUgrep
2.6
Commentaires
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
etxargs -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é
grep
peut supprimer les tampons quil a traités jusquà présent. Vous pouvezgrep
la sortie deyes
indéfiniment sans utiliser plus de quelques kilo-octets de mémoire. Le problème est la taille des lignes.--null-data
peut également être utile ici. Cela force lutilisation de NUL au lieu de la nouvelle ligne comme terminateur de ligne dentrée.dd if=/dev/sda | fold -b $((4096*1024*1024)) | grep -a "some string"
pour limiter la quantité de mémoire requise à 4 Go