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 GNU grep 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 Sie grep -r mit neuerer GNU grep 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. GNU grep im Gegensatz zu vielen anderen grep 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 GNU grep 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önnen grep die Ausgabe von yes 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 und xargs -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

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.