Ich habe eine sehr einfache Suche durchgeführt:
grep -R Milledgeville ~/Documents
Und Nach einiger Zeit trat dieser Fehler auf:
grep: memory exhausted
Wie kann ich dies vermeiden?
Ich habe 10 GB RAM auf meinem System und wenige Anwendungen Ich bin wirklich überrascht, dass einem einfachen Grep der Speicher ausgeht. ~/Documents
hat ungefähr 100 GB und enthält alle Arten von Dateien.
grep -RI
hat dieses Problem möglicherweise nicht, aber ich möchte Sie können auch in Binärdateien suchen.
Antwort
Zwei mögliche Probleme:
-
grep -R
(mit Ausnahme der modifizierten GNUgrep
unter OS / X 10.8 und höher) folgt Symlinks, auch wenn es nur solche gibt Bei 100 GB Dateien in~/Documents
ist möglicherweise immer noch ein Symlink zu/
vorhanden, und Sie scannen am Ende das gesamte Dateisystem einschließlich Dateien wie/dev/zero
. Verwenden Siegrep -r
mit neuerer GNUgrep
oder verwenden Sie die Standardsyntax:find ~/Documents -type f -exec grep Milledgeville /dev/null {} +
(beachten Sie jedoch, dass der Exit-Status nicht die Tatsache widerspiegelt, dass das Muster übereinstimmt oder nicht).
-
grep
findet die Zeilen, die dem Muster entsprechen. Dazu muss jeweils eine Zeile in den Speicher geladen werden. GNUgrep
im Gegensatz zu vielen anderengrep
Implementierungen haben keine Begrenzung für die Größe der gelesenen Zeilen und unterstützen die Suche in Binärdateien. Wenn Sie also eine Datei mit einer sehr großen Zeile (dh mit zwei Zeilenumbrüchen, die sehr weit voneinander entfernt sind) haben, die größer als der verfügbare Speicher ist, schlägt dies fehl.Dies passiert normalerweise mit a Datei mit geringer Dichte. Sie können sie reproduzieren mit:
truncate -s200G some-file grep foo some-file
Diese Datei ist schwer zu umgehen. Sie können dies wie folgt tun (immer noch mit GNU ):
find ~/Documents -type f -exec sh -c "for i do tr -s "\0" "\n" < "$i" | grep --label="$i" -He "$0" done" Milledgeville {} +
Konvertiert Sequenzen von NUL-Zeichen in ein Zeilenumbruchzeichen, bevor die Eingabe an . Dies gilt für Fälle, in denen das Problem auf spärliche Dateien zurückzuführen ist.
Sie können es optimieren, indem Sie es nur für große Dateien ausführen:
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 {} + \)
Wenn die Dateien nicht dünn sind und Sie eine Version von GNU
grep
vor können Sie die Option--mmap
verwenden. Die Zeilen werden im Speicher zugeordnet und nicht dort kopiert, was bedeutet, dass das System den Speicher jederzeit zurückfordern kann y durch Ausblättern der Seiten in die Datei. Diese Option wurde in GNUgrep
2.6
Kommentare
- entfernt @GodricSeer, es kann immer noch einen großen Teil der Datei in einen einzelnen Puffer lesen, aber wenn es nicht ‚ ist, findet es die Zeichenfolge dort nicht und hat ‚ Ich habe auch kein Zeilenumbruchzeichen gefunden. Ich wette, dass dieser einzelne Puffer im Speicher bleibt und der nächste Puffer eingelesen wird, da er angezeigt werden muss, wenn eine Übereinstimmung gefunden wird. Das Problem ist also immer noch dasselbe. In der Praxis schlägt ein Grep in einer 200-GB-Sparse-Datei mit OOM fehl.
- @GodricSeer, also nein. Wenn alle Zeilen klein sind, kann
grep
die bisher verarbeiteten Puffer verwerfen. Sie könnengrep
die Ausgabe vonyes
unbegrenzt ausführen, ohne mehr als ein paar Kilobyte Speicher zu verwenden. Das Problem ist die Größe der Zeilen. - Die Option GNU grep
--null-data
kann auch hier hilfreich sein. Es erzwingt die Verwendung von NUL anstelle von Newline als Eingangsleitungsabschluss. - @ 1_CR, guter Punkt, obwohl dadurch auch der Ausgangsleitungsabschluss auf NUL gesetzt wird id = „65d0f81c55“>
fold Befehlshilfe in solchen Situationen? Denken Sie beispielsweise andd if=/dev/sda | fold -b $((4096*1024*1024)) | grep -a "some string"
, um die erforderliche Speichermenge auf 4 GB zu beschränken.
Antwort
Normalerweise mache ich
find ~/Documents | xargs grep -ne "expression"
Ich habe eine Reihe von Methoden ausprobiert und festgestellt, dass dies die schnellste ist. Beachten Sie, dass dies Dateien mit Leerzeichen und dem Dateinamen nicht sehr gut verarbeitet. Wenn Sie wissen, dass dies der Fall ist und eine GNU-Version von grep haben, können Sie Folgendes verwenden:
find ~/Documents -print0 | xargs -0 grep -ne "expression"
Wenn nicht, können Sie Folgendes verwenden:
find ~/Documents -exec grep -ne "expression" "{}" \;
Damit wird exec
ein grep für jede Datei.
Kommentare
- Dies wird bei Dateien mit Leerzeichen unterbrochen.
- Hmm, das ist wahr.
- Sie können dies mit
find -print0 | xargs -0 grep -ne 'expression'
- @ChrisDown umgehen. Dies ist eher eine nicht protable Lösung als eine defekte tragbare Lösung.
- @ChrisDown Most Die wichtigsten Einheiten haben inzwischen
find -print0
undxargs -0
übernommen: alle drei BSD, MINIX 3, Solaris 11,…
Antwort
Ich kann mir einige Möglichkeiten vorstellen, um dies zu umgehen:
-
Stattdessen Um alle Dateien gleichzeitig zu erfassen, führen Sie jeweils eine Datei aus.Beispiel:
find /Documents -type f -exec grep -H Milledgeville "{}" \;
-
Wenn Sie nur wissen müssen, welche Dateien die Wörter enthalten, führen Sie
grep -l
stattdessen. Da grep dort nach dem ersten Treffer aufhört zu suchen, muss es keine großen Dateien mehr lesen. -
Wenn Sie auch den eigentlichen Text möchten, können Sie zwei Zeichenfolgen eingeben separate Greps entlang:
for file in $( grep -Rl Milledgeville /Documents ); do grep -H Milledgeville "$file"; done
Kommentare
- Das letzte Beispiel ist keine gültige Syntax – Sie ‚ müssen eine Befehlssubstitution durchführen (und Sie sollten ‚ dies nicht tun, da
grep
gibt ein Trennzeichen aus, das in Dateinamen zulässig ist. Sie müssen auch$file
angeben. - Das letztere Beispiel leidet Bei Dateinamen mit Zeilenumbruch oder Leerzeichen (
for
verarbeitet die Datei als zwei Argumente) - @DravSloan Ihre Bearbeitung, während Eine Verbesserung, die immer noch bei legalen Dateinamen bricht.
- Ja, ich habe sie belassen, weil sie Teil ihrer Antwort war. Ich habe nur versucht, sie zu verbessern, damit sie ausgeführt wird (für die Fälle, in denen ich dort bin s keine Leerzeichen / Zeilenumbrüche usw. in Dateien).
- Korrekturen von seiner – > ihr, ich entschuldige mich Jenny: /
Antwort
Ich greife nach einer 6-TB-Festplatte, um nach verlorenen Daten zu suchen, und habe den Speicher erschöpft. Dies sollte auch für andere Dateien funktionieren.
Die Lösung bestand darin, die Festplatte mit dd in Blöcken zu lesen und die Blöcke zu erfassen. Dies ist der 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
Kommentare
- Es sei denn, Sie lesen Bei überlappenden Blöcken würden Sie möglicherweise Übereinstimmungen an den Blockgrenzen verpassen. Die Überlappung muss mindestens so groß sein wie die Zeichenfolge, mit der Sie voraussichtlich übereinstimmen.
- Aktualisiert, um 1 MB mehr in jedem 100-MB-Block zu suchen … billiger Hack