Grep in paar tausend Dateien

13

Ich habe ein Verzeichnis mit ca. 26.000 Dateien und muss alle diese Dateien einlesen. Das Problem ist, dass ich es so schnell wie möglich brauche, daher ist es nicht ideal, ein Skript zu erstellen, in dem grep den Namen einer Datei aus dem Befehl find entnimmt und Übereinstimmungen in die Datei schreibt. Bevor das Problem "Argumente sind zu lang" auftrat, dauerte es ca. 2 Minuten, bis alle diese Dateien eingespielt waren. Irgendwelche Ideen, wie es geht? Bearbeiten: Es gibt ein Skript, das ständig neue Dateien erstellt. Daher ist es nicht möglich, alle Dateien in verschiedenen Verzeichnissen abzulegen.

user2778979
quelle
1
Verwenden Sie findmit xargsodergrep -R
Eddy_Em
Es funktioniert gut, aber es dauert 10 Minuten ...
user2778979

Antworten:

19

Mit find:

cd /the/dir
find . -type f -exec grep pattern {} +

( Dient -type fnur zum Suchen in regulären Dateien (auch ohne Symlinks, selbst wenn sie auf reguläre Dateien verweisen). Wenn Sie in einem beliebigen Dateityp außer Verzeichnissen suchen möchten (aber beachten Sie, dass es einige Dateitypen wie fifos oder / dev / zero gibt) im allgemeinen mögen Sie nicht lesen), ersetzen Sie -type fmit dem GNU-spezifischen ! -xtype d( -xtype dEinstimmungen für Dateien vom Typ Verzeichnis nach Symlink Auflösung)).

Mit GNU grep:

grep -r pattern /the/dir

(Beachten Sie jedoch, dass, sofern Sie keine neuere Version von GNU grep haben, beim Abstieg in Verzeichnisse Symlinks folgen). Nicht reguläre Dateien werden nur durchsucht, wenn Sie eine -D readOption hinzufügen . Neuere Versionen von GNU grepwerden jedoch immer noch nicht in Symlinks suchen.

Sehr alte Versionen von GNU findhaben die Standardsyntax nicht unterstützt {} +, aber dort können Sie die nicht standardmäßige verwenden:

cd /the/dir &&
  find . -type f -print0 | xargs -r0 grep pattern

Performances sind wahrscheinlich I / O-gebunden. Dies ist die Zeit, die für die Suche benötigt wird, um alle diese Daten aus dem Speicher zu lesen.

Wenn sich die Daten auf einem redundanten Festplattenarray befinden, kann das gleichzeitige Lesen mehrerer Dateien die Leistung verbessern (und sie ansonsten beeinträchtigen). Wenn die Leistung nicht an E / A gebunden ist (weil sich beispielsweise alle Daten im Cache befinden) und Sie über mehrere CPUs verfügen, kann die gleichzeitige grepsAusführung ebenfalls hilfreich sein. Sie können dies mit xargsder -POption von GNU tun .

Zum Beispiel, wenn sich die Daten auf einem RAID1-Array mit 3 Laufwerken befinden oder wenn sich die Daten im Cache befinden und Sie 3 CPUs haben, deren Zeit übrig bleibt:

cd /the/dir &&
  find . -type f -print0 | xargs -n1000 -r0P3 grep pattern

(Hier wird alle 1000 Dateien -n1000eine neue grepDatei erzeugt, bis zu 3 gleichzeitig).

Beachten Sie jedoch, dass Sie, wenn die Ausgabe von grepumgeleitet wird, eine schlecht verschachtelte Ausgabe der drei grepProzesse erhalten. In diesem Fall möchten Sie sie möglicherweise ausführen als:

find . -type f -print0 | stdbuf -oL xargs -n1000 -r0P3 grep pattern

(auf einem aktuellen GNU- oder FreeBSD-System) oder verwenden Sie die --line-bufferedOption von GNU grep.

Wenn patternes sich um eine feste Zeichenfolge handelt, kann das Hinzufügen der -FOption die Situation verbessern.

Wenn es sich nicht um Mehrbytezeichendaten handelt oder wenn für die Übereinstimmung dieses Musters keine Rolle spielt, ob es sich um Mehrbytezeichen handelt oder nicht, dann gilt Folgendes:

cd /the/dir &&
  LC_ALL=C grep -r pattern .

könnte die Leistung erheblich verbessern.

Wenn Sie solche Suchanfragen häufig durchführen, möchten Sie Ihre Daten möglicherweise mithilfe einer der vielen Suchmaschinen indizieren.

Stéphane Chazelas
quelle
3

26000 Dateien in einem einzigen Verzeichnis sind für die meisten Dateisysteme eine Menge. Es ist wahrscheinlich, dass das Lesen dieses großen Verzeichnisses einen erheblichen Teil der Zeit in Anspruch nimmt. Teilen Sie es in kleinere Verzeichnisse mit jeweils nur ein paar hundert Dateien auf.

Ein Anruf findkann eine schlechte Leistung nur erklären, wenn Sie es falsch machen. Auf diese Weise können Sie schnell ein Verzeichnis durchsuchen und sicherstellen, dass Sie nicht riskieren, eine zu lange Befehlszeile auszuführen. Stellen Sie sicher, dass Sie verwenden -exec grep PATTERN {} +, dass so viele Dateien wie möglich pro Befehlsaufruf gepackt werden und nicht -exec grep PATTERN {} \;, dass grepder Befehl einmal pro Datei ausgeführt wird: Die Ausführung des Befehls einmal pro Datei ist wahrscheinlich erheblich langsamer.

Gilles 'SO - hör auf böse zu sein'
quelle
Danke, ich werde etwas darüber googeln und das werde ich wahrscheinlich aufteilen. Ich habe genau das gemacht, worüber Sie schreiben, und es dauerte dreimal länger als nur grep ...
user2778979
Gilles, sagen Sie, dass sich die Leistung für 26.000 Dateien in einem Verzeichnis erheblich von 26.000 Dateien unterscheidet, die auf beispielsweise 100 Verzeichnisse verteilt sind?
User001
1
@ user001 Ja. Inwieweit sie sich unterscheiden, hängt vom Dateisystem und möglicherweise vom zugrunde liegenden Speicher ab. Ich würde jedoch davon ausgehen, dass jedes Dateisystem mit 260 Dateien in 100 Verzeichnissen messbar schneller ist als mit 26000 Dateien in einem einzelnen Verzeichnis.
Gilles 'SO- hör auf böse zu sein'
Danke für die Klarstellung. Ich fragte eine Follow-up - Frage in diesem Punkt , um die Grundlage für die Diskrepanz zu verstehen.
User001
0

Wenn Sie ALLE Dateien mehrmals durchsuchen müssen (wie Sie sagten, indem Sie ein Skript ausführen), würde ich vorschlagen, dass Sie sich die RAM-Datenträger ansehen, alle Dateien dort kopieren und die Dateien dann mehrmals durchsuchen. Dies beschleunigt Ihre Suche um den Faktor 1 mindestens 100x.

Du brauchst nur genug RAM. Andernfalls sollten Sie sich mit der Indizierung der Dateien befassen, z. in lucene oder eine nosql-datenbank und dann abfragen dazu ausführen.

Tobias Feldballe
quelle
Wie bereits an anderer Stelle erwähnt, hilft dies nicht, wenn zu viele Dateien vorhanden sind, um eine Datei auszuführen grep. Es gibt auch den Punkt, dass: "Es gibt ein Skript, das ständig neue Dateien erstellt, so dass es nicht möglich ist, alle Dateien in verschiedenen Verzeichnissen abzulegen."
Jeff Schaller
-2

Alle Dateien im Verzeichnis

grep 'search string' *

mit rekursiv

grep -R 'search string' *
Markus
quelle
Möchtest du die -1 ausarbeiten?
Markus
4
Ich habe nicht abgelehnt, aber es gibt ein paar Probleme mit Ihnen: Das OP erwähnte eine "Argument-Liste zu lang", die Ihr erstes Problem nicht behebt und wahrscheinlich das ist, was das OP zuvor getan hat. Der zweite hilft auch in dieser Hinsicht nicht (hätte geholfen, wenn Sie ihn .anstelle von verwendet hätten *). *schließt Punktdateien aus (obwohl mit -R, nicht die in den rekursiven Verzeichnissen). -R im Gegensatz zu -r folgt Symlinks auch mit neueren Versionen von GNU grep. Sie haben auch ein Problem mit Dateien im aktuellen Verzeichnis, deren Name mit-
Stéphane Chazelas