Ich habe eine Datei, die ungefähr 200.000 Zeilen pro Tag umfasst und die aus Blöcken mit drei Zeilen besteht:
1358726575123 # key
Joseph Muller # name
carpenter # job
9973834728345
Andres Smith
student
7836472098652
Mariah Anthony
dentist
Jetzt habe ich eine andere Datei, aus der ich ungefähr 10.000 Schlüsselmuster extrahiere, wie z 1358726575123
. Dann führe ich eine for
Schleife mit diesen Mustern durch und muss sie mit der ersten Datei vergleichen. Wenn die Datei kein solches Muster enthält, speichere ich das Muster zur weiteren Verarbeitung in einer dritten Datei:
for number in $(grep -o '[0-9]\{12\}' file2); do # finds about 10.000 keys
if ! grep -q ^$number$ file1; then # file1 is a huge file
printf "$number\n" >>file3 # we'll process file3 later
fi
done
Der Beispielcode greift 10.000 Mal nach einer riesigen Datei, und ich führe diese Schleife den ganzen Tag über etwa einmal pro Minute aus .
Was kann ich tun, um all das zu beschleunigen und CPU zu sparen, da die riesige Datei immer größer wird? Ich frage mich, ob es hilfreich ist, die Datei irgendwie nach ihrem Schlüssel zu sortieren (wenn ja, wie?) Oder eine Datenbank anstelle von einfachem Text zu verwenden ...
Antworten:
Diese Antwort basiert auf der
awk
Antwort von potong .Sie ist doppelt so schnell wie die
comm
Methode (auf meinem System), für die gleichen 6 Millionen Zeilen in der Hauptdatei und 10 Tausend Schlüssel ... (jetzt aktualisiert, um FNR zu verwenden, NR)Obwohl
awk
es schneller ist als Ihr aktuelles System und Ihnen und Ihren Computern eine gewisse Atempause einräumt, sollten Sie sich bewusst sein, dass Sie bei einer so intensiven Datenverarbeitung wie beschrieben die besten Gesamtergebnisse erzielen, wenn Sie zu einer dedizierten Datenbank wechseln. z.B. SQlite, MySQL ...quelle
file1 -> mainfile
undfile2 -> keys
mit gawk und mawk, und es gibt falsche Schlüssel aus.awk
Sie eine Reihe von Dateien einlesen. In diesem Fall enthält diese Reihe 3 Dateien. Die Ausgabe erfolgt anstdout
mainfile
, und es wird auch keine Schlüssel aus derkeys
Druckdatei , die ist nicht inmainfile
... Das ist wahrscheinlich das, was passiert ist ... (Ich werde ein bisschen weiter schauen hinein ...$RANDOM
zum Hochladen zu erstellen .Das Problem ist natürlich, dass Sie grep 10.000 Mal auf der großen Datei ausführen. Sie sollten beide Dateien nur einmal lesen. Wenn Sie außerhalb der Skriptsprachen bleiben möchten, können Sie dies folgendermaßen tun:
comm
die sortierten Listen aus, um zu ermitteln, was nur auf der zweiten Liste stehtEtwas wie das:
Sehen
man comm
.Wenn Sie die große Datei jeden Tag kürzen könnten (wie eine Protokolldatei), könnten Sie einen Cache mit sortierten Zahlen führen und müssten sie nicht jedes Mal vollständig analysieren.
quelle
{12}
.. OP hat 12 verwendet, aber die Beispielschlüssel sind 13 lang ...<(grep...sort)
die Dateinamen angeben.tail -n +$linenum
der nur die neuesten Daten ausgegeben werden. Auf diese Weise werden Sie nur ungefähr 200.000 Zeilen pro Tag verarbeiten. Ich habe es gerade mit 6 Millionen Zeilen in der Hauptdatei und 10.000 Schlüsseln getestet. Zeit : real 0m0.016s, user 0m0.008s, sys 0m0.008sJa, auf jeden Fall eine Datenbank verwenden. Sie sind genau für solche Aufgaben gemacht.
quelle
Das könnte für Sie funktionieren:
BEARBEITEN:
Geändertes Skript, um Duplikate und unbekannte Schlüssel in beiden Dateien zuzulassen, erzeugt weiterhin Schlüssel aus der ersten Datei, die in der zweiten nicht vorhanden sind:
quelle
Mit so vielen Daten sollten Sie wirklich zu einer Datenbank wechseln. In der Zwischenzeit müssen Sie nicht
file1
für jeden Schlüssel einzeln suchen, um eine annähernd anständige Leistung zu erzielen . Führen Sie einen einzelnen ausgrep
, um alle nicht ausgeschlossenen Schlüssel auf einmal zu extrahieren. Da diesgrep
auch Zeilen zurückgibt, die keinen Schlüssel enthalten, filtern Sie diese weg.(
-Fx
bedeutet wörtlich, ganze Zeilen zu durchsuchen.-f -
bedeutet, eine Liste von Mustern aus der Standardeingabe zu lesen.)quelle
-v
(-Fxv
) kann sich darum kümmern.comm
bevorzuge .Gestatten Sie mir, das zu bekräftigen, was andere gesagt haben: "Bring dich in eine Datenbank!"
Es gibt MySQL-Binärdateien, die für die meisten Plattformen frei verfügbar sind.
Warum nicht SQLite? Es ist speicherbasiert und lädt eine Flat-Datei, wenn Sie sie starten, und schließt sie, wenn Sie fertig sind. Dies bedeutet, dass wenn Ihr Computer abstürzt oder der SQLite-Prozess nicht mehr ausgeführt wird, auch alle Daten.
Ihr Problem sieht aus wie ein paar Zeilen SQL und wird in Millisekunden ausgeführt!
Nach der Installation von MySQL (was ich vor anderen Optionen empfehle) würde ich 40 US-Dollar für O'Reillys SQL-Kochbuch von Anthony Molinaro ausgeben, das viele Problemmuster aufweist, angefangen bei einfachen
SELECT * FROM table
Abfragen bis hin zu Aggregaten und mehreren Verknüpfungen.quelle
Ich bin mir nicht sicher, ob dies die genaue Ausgabe ist, nach der Sie suchen, aber der wahrscheinlich einfachste Weg ist:
Sie könnten auch verwenden:
Jede dieser Dateien erstellt eine temporäre Musterdatei, mit der die Zahlen aus der großen Datei (
file1
) abgerufen werden .quelle
grep -vf
anstelle von verwendengrep -f
.Ich bin völlig damit einverstanden, dass Sie eine Datenbank erhalten (MySQL ist ziemlich einfach zu bedienen). Bis Sie das zum Laufen bringen, mag ich Angus '
comm
Lösung, aber so viele Leute versuchen esgrep
und verstehen es falsch, dass ich dachte, ich zeige den (oder zumindest einen) richtigen Weg, um es zu tungrep
.Der erste
grep
bekommt die Schlüssel. Der drittegrep
(in<(...)
) nimmt alle in der großen Datei verwendeten Schlüssel und<(...)
übergibt sie wie eine Datei als Argument an-f
den zweiten grep. Das führt dazu, dass die zweitegrep
Zeile sie als Liste der übereinstimmenden Zeilen verwendet. Anschließend wird dies verwendet, um die Eingabe (die Liste der Schlüssel) aus der Pipe abzugleichen (zuerstgrep
) und alle Schlüssel zu drucken, die aus der Schlüsseldatei und nicht aus-v
der großen Datei extrahiert wurden .Natürlich können Sie dies mit temporären Dateien tun, die Sie im Auge behalten und nicht vergessen müssen, zu löschen:
Hiermit werden alle Zeilen gedruckt, in
allkeys
denen nicht angezeigt wirdusedkeys
.quelle
grep: Memory exhausted
comm
, in dieser Reihenfolge.Die Schlüsseldatei ändert sich nicht? Dann sollten Sie es vermeiden, die alten Einträge immer wieder zu durchsuchen.
Mit
tail -f
können Sie die Ausgabe einer wachsenden Datei erhalten.grep -f liest die Muster aus einer Datei, eine Zeile als Muster.
quelle
Ich wollte meine Antwort nicht veröffentlichen, da ich der Meinung war, dass eine solche Datenmenge nicht mit einem Shell-Skript verarbeitet werden sollte und die richtige Antwort für die Verwendung einer Datenbank bereits gegeben wurde. Aber seitdem gibt es 7 andere Ansätze ...
Liest die erste Datei im Speicher, durchsucht dann die zweite Datei nach Zahlen und prüft, ob Werte im Speicher gespeichert sind. Es sollte schneller als ein Vielfaches von
grep
s sein, wenn Sie genug Speicher haben, um die gesamte Datei zu laden.quelle
Ich stimme @ jan-steinman zu, dass Sie für diese Art von Aufgabe eine Datenbank verwenden sollten. Wie die anderen Antworten zeigen, gibt es viele Möglichkeiten, eine Lösung mit einem Shell-Skript zu hacken. Wenn Sie den Code jedoch länger als 15 Minuten verwenden und warten, kann dies zu erheblichen Problemen führen nur ein eintägiges wegwerfprojekt.
Angenommen, Sie arbeiten auf einer Linux-Box, dann ist höchstwahrscheinlich standardmäßig Python installiert, das die sqlite3-Bibliothek ab Python v2.5 enthält. Sie können Ihre Python-Version überprüfen mit:
Ich empfehle die Verwendung von SQLite3-Bibliothek da es sich um eine einfache dateibasierte Lösung handelt, die für alle Plattformen (einschließlich Ihres Webbrowsers) verfügbar ist und keine Installation eines Servers erfordert. Im Wesentlichen konfigurations- und wartungsfrei.
Im Folgenden finden Sie ein einfaches Python-Skript, das das von Ihnen als Beispiel angegebene Dateiformat analysiert und anschließend eine einfache Abfrage "Alles auswählen" durchführt und alles ausgibt, was in der Datenbank gespeichert ist.
Ja, dies bedeutet, dass Sie etwas SQL lernen müssen , aber es wird sich auf lange Sicht lohnen. Anstatt Ihre Protokolldateien zu analysieren, können Sie auch Daten direkt in Ihre SQLite-Datenbank schreiben.
quelle
/usr/bin/sqlite3
funktioniert genauso für Shell-Skripte ( packages.debian.org/squeeze/sqlite3 ), obwohl ich es nie benutzt habe./usr/bin/sqlite3
Shell-Skripte verwenden. Ich empfehle jedoch, Shell-Skripte zu vermeiden, außer für einfache Wegwerfprogramme. Verwenden Sie stattdessen eine Sprache wie Python, die eine bessere Fehlerbehandlung aufweist und einfacher zu warten und zu erweitern ist.