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.
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
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.
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
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