Verwenden von 'head' oder 'tail' für eine riesige Textdatei - 19 GB

9593
nicorellius

Ich habe ein Problem mit dem Anzeigen von Blöcken einer sehr großen Textdatei. Diese Datei (ca. 19 GB) ist offensichtlich zu groß, um sie auf herkömmliche Weise anzuzeigen.

Ich habe versucht head 1und tail 1( head -n 1und tail -n 1) mit beiden Befehlen, die auf verschiedene Weise miteinander verbunden sind (um ein Stück in der Mitte zu erreichen), ohne Glück. Mein Linux-Computer mit Ubuntu 9.10 kann diese Datei nicht verarbeiten.

Wie gehe ich mit dieser Datei um? Mein ultimatives Ziel ist es, die Linien 45000000 und 45000100 zu verbessern.

13
Ich habe mir überlegt, ein schnelles Python-Skript zu schreiben, um die Zeilen zu lesen und die zu drucken, die ich zur Ablage benötige. Ich kann mir jedoch vorstellen, dass dies lange dauert ... nicorellius vor 12 Jahren 0
Sind alle Linien gleich lang? Paul vor 12 Jahren 0
@ Paul - leider haben sie nicht die gleiche Länge. nicorellius vor 12 Jahren 0
Sie können versuchen, [`split`] (http://linux.die.net/man/1/split) zu verwenden, um die Arbeit mit der großen Datei zu erleichtern. iglvzx vor 12 Jahren 0
OK. Jede Verarbeitung einer so großen Datei wird einige Zeit in Anspruch nehmen, daher werden die folgenden Antworten hilfreich sein. Wenn Sie nur das gewünschte Teil extrahieren möchten und ungefähr abschätzen können, wo es sich befindet, können Sie `dd` verwenden, um das gewünschte Bit zu erhalten. Zum Beispiel: "dd if = bigfile of = extractfile bs = 1M überspringen = 10240 count = 5" extrahiert 5 MB aus der Datei ab dem 10-GB-Punkt. Paul vor 12 Jahren 1
Ja, ich stimme dir zu, Paul. Ich habe ein Python-Skript geschrieben und es dauerte definitiv ewig, die Datei zu verarbeiten. Ich habe jetzt den "sed" Job und ich kann mir vorstellen, dass es eine Weile dauern wird. Das Testen mit dem Anfang der Datei erscheint jedoch vielversprechend. Vielen Dank. nicorellius vor 12 Jahren 0

4 Antworten auf die Frage

11
Kyle Jones

Sie sollten verwenden sed.

sed -n -e 45000000,45000100p -e 45000101q bigfile > savedlines 

Dies bedeutet sed, Zeilen 45000000-45000100 einschließlich zu drucken und in Zeile 45000101 zu beenden.

Es ist immer noch sehr langsam, fast wie ein Kopf -45000000,45000100p bigfile | Schwanz -100> gespeicherte Zeilen Dmitry Polushkin vor 9 Jahren 1
`tail + | head` ist um gute 10-15% schneller. erich vor 6 Jahren 0
4
Der Hochstapler

Erstellen Sie eine MySQL-Datenbank mit einer einzelnen Tabelle, die ein einzelnes Feld enthält. Dann importieren Sie Ihre Datei in die Datenbank. Dies macht es sehr einfach, eine bestimmte Zeile nachzuschlagen.

Ich glaube nicht, dass irgendetwas anderes schneller sein könnte (wenn headund tailschon versagen). Am Ende muss die Anwendung, die Zeile nsuchen will, die gesamte Datei ndurchsuchen, bis sie Zeilenumbrüche gefunden hat. Ohne eine Art Nachschlagen (Zeilenindex zu Byte-Offset in Datei) kann keine bessere Leistung erzielt werden.

In Anbetracht dessen, wie einfach es ist, eine MySQL-Datenbank zu erstellen und Daten in diese Datenbank zu importieren, halte ich dies für einen gangbaren Ansatz.

So wird's gemacht:

DROP DATABASE IF EXISTS helperDb; CREATE DATABASE `helperDb`; CREATE TABLE `helperDb`.`helperTable`( `lineIndex` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `lineContent` MEDIUMTEXT, PRIMARY KEY (`lineIndex`) ); LOAD DATA INFILE '/tmp/my_large_file' INTO TABLE helperDb.helperTable (lineContent); SELECT lineContent FROM helperTable WHERE ( lineIndex > 45000000 AND lineIndex < 45000100 ); 

/tmp/my_large_file wäre die Datei, die Sie lesen möchten.

Die korrekte Syntax zum Importieren einer Datei mit durch Tabulatorzeichen getrennten Werten in jeder Zeile lautet:

LOAD DATA INFILE '/tmp/my_large_file' INTO TABLE helperDb.helperTable FIELDS TERMINATED BY '\n' (lineContent); 

Ein weiterer großer Vorteil ist, dass Sie, wenn Sie sich später entscheiden, einen anderen Satz von Zeilen zu extrahieren, keine Stunden auf die Verarbeitung warten müssen (es sei denn, Sie löschen natürlich die Datenbank).

Dies ist in der Tat eine gute Lösung. Ich habe es mit dem `sed`-Befehl unten arbeiten lassen und meine Zeilen identifiziert. Aber jetzt habe ich eine Folgefrage, für die die Datenbankmethode möglicherweise besser geeignet ist. Ich muss jetzt ein paar hundert Zeilen aus der Datei löschen. nicorellius vor 12 Jahren 0
Ich bin mir sicher, dass "sed" das auch tun könnte. Wenn Sie die Daten in der Datenbank hätten, wäre es natürlich trivial, eine neue Datei mit den gewünschten Zeilen zu exportieren. Der Hochstapler vor 12 Jahren 0
Danke noch einmal. Ich nahm die Antwort "sed" (weil sie mir unmittelbarere Freude bereitete; -), gab Ihnen aber eine positive Stimme, weil ich Ihre Methode in der Zukunft verwenden werde. Ich schätze es. nicorellius vor 12 Jahren 0
Ich habe versucht, den oben genannten SQL-Code zu verwenden, und er schien zu verarbeiten, aber als ich die Abfrage ausgeführt habe, um meine Zeilen anzuzeigen, wurde mir nur die erste Spalte der tabulatorbegrenzten Zeile angezeigt. Jede der Zeilen ist durch Tabulatoren getrennt. Gibt es einen Ratschlag, den Sie mir geben könnten, um alle Zeilen wie erwartet in den Tisch zu bekommen? nicorellius vor 12 Jahren 0
Sie können versuchen, der Zeile [`LOAD DATA] (http://dev.mysql.com/doc/refman/5.1/en/load-data.html) die Option 'FIELDS TERMINATED BY' \ n'` hinzuzufügen. Der Hochstapler vor 12 Jahren 1
OK danke. Nicht vertraut mit dieser Syntax, aber es wird ein Fehler angezeigt, wenn Folgendes verwendet wird: LOAD DATA INFILE / tmp / my_large_file INTO TABLE helperTable (lineContent) FIELDS TERMINATED BY '\ n'; ` Herausspringen. Irgendwelche Gedanken? Es tut mir leid, Sie damit zu belästigen. nicorellius vor 12 Jahren 0
Es tut mir leid, es ist ein Fehler in meinem Code aufgetreten. Ich habe auch die richtige Syntax für Ihren Fall hinzugefügt (diesmal getestet). Der Hochstapler vor 12 Jahren 1
Toll - danke - ich werde das heute später testen. Schätze deine Hilfe. nicorellius vor 12 Jahren 0
1
Anssi

Zwei gute alte Werkzeuge für große Dateien sind joinund split. Sie können die --lines=<number>Option split with verwenden, um Dateien in mehrere Dateien bestimmter Größe zu schneiden.

Zum Beispiel split --lines=45000000 huge_file.txt. Die resultierenden Teile würden in xa, xb usw. sein. Dann können Sie headden Teil xb verwenden, der die gewünschten Zeilen enthalten würde. Sie können Dateien auch zu einer einzelnen großen Datei zusammenfügen.

Toll, danke, ich habe den Split-Befehl total vergessen. siliconrockstar vor 7 Jahren 0
0
erich

Sie haben die richtigen Werkzeuge, verwenden sie jedoch falsch. Wie bereits beantwortet bei U & L über, tail -n +X file | head -n Y(die Note +) ist 10-15% schneller als sedfür Y - Leitungen an X. Starten und bequem, müssen Sie sich nicht explizit auf exitdas Verfahren, wie mit sed.

tail liest und verwirft die ersten X-1-Zeilen (es gibt keine Möglichkeit, dies zu umgehen), dann werden die folgenden Zeilen gelesen und gedruckt. head liest und druckt die angeforderte Zeilenzahl und geht dann aus. Wenn der Kopf beendet wird, empfängt das Endstück ein SIGPIPE-Signal und stirbt, so dass es nicht mehr als die Größe einer Puffergröße (normalerweise einige Kilobytes) von Zeilen aus der Eingabedatei gelesen hat.