Hier ist mein Problem, ich habe eine Reihe von großen gz
Protokolldateien, die allererste Info in der Zeile ist ein Datetime-Text, z. B .: 2014-03-20 05:32:00.
Ich muss prüfen, welche Protokolldateien bestimmte Daten enthalten. Für die Init mache ich einfach ein:
%Vor%ABER WIE man dasselbe mit der letzten Zeile macht, ohne die ganze Datei zu bearbeiten, wie es mit zcat (zu schwer) gemacht würde:
%Vor%Zusätzliche Informationen, diese Protokolle werden mit der Datenzeit des ersten Datensatzes erstellt. Wenn ich also die Protokolle um 14:00:00 abfragen möchte, muss ich auch in Dateien suchen, die VOR 14:00:00 erstellt wurden Eine Datei wird um 13:50:00 erstellt und um 14:10:00 Uhr geschlossen.
Die einfachste Lösung wäre, Ihre Protokollrotation zu ändern, um kleinere Dateien zu erstellen.
Die zweit einfachste Lösung wäre ein Komprimierungstool, das wahlfreien Zugriff unterstützt.
Projekte wie diczip , BGZF und csio Jeder fügt Flush-Punkte in verschiedenen Intervallen in gzip-komprimierten Daten hinzu, mit denen Sie suchen können in einem Programm, das diese zusätzlichen Informationen kennt. Obwohl es im Standard vorhanden ist, fügt die Vanilla gzip
solche Marker entweder standardmäßig oder als Option nicht hinzu.
Dateien, die von diesen Dienstprogrammen mit wahlfreiem Zugriff komprimiert wurden, sind etwas größer (um etwa 2-20%) aufgrund der Marker selbst, unterstützen aber vollständig die Dekomprimierung mit gzip
oder ein anderes Dienstprogramm, das diese Marker nicht kennt.
Sie können bei dieser Frage mehr über random lernen Zugriff in verschiedenen Komprimierungsformaten .
Es gibt auch einen Blog "Blasted Bioinformatics" von Peter Cock mit mehreren Beiträgen zu diesem Thema, einschließlich:
xz
xz
(ein LZMA Komprimierungsformat) hat tatsächlich eine Direktzugriffsunterstützung auf Blockebene, aber Sie erhalten nur einen einzigen Block mit den Standardeinstellungen.
xz
kann mehrere Archive miteinander verketten. In diesem Fall hätte jedes Archiv seinen eigenen Block. Die GNU split
kann dies leicht tun:
Das weist split
an, big.log
in 50MB-Blöcke zu zerlegen ( vor -Komprimierung) und jede durch xz -c
auszuführen, wodurch der komprimierte Chunk zur Standardausgabe ausgegeben wird. Wir sammeln dann diese Standardausgabe in eine einzige Datei namens big.log.sp.xz
.
Um dies ohne GNU zu tun, benötigen Sie eine Schleife:
%Vor% Sie können die Liste der Blockoffsets mit xz --verbose --list FILE.xz
erhalten. Wenn Sie den letzten Block benötigen, benötigen Sie die komprimierte Größe (Spalte 5) plus 36 Byte für den Overhead (gefunden durch Vergleichen der Größe mit hd big.log.sp0.xz |grep 7zXZ
). Holen Sie diesen Block mit tail -c
und leiten Sie ihn durch xz
. Da die obige Frage die letzte Zeile der Datei haben möchte, übergebe ich sie durch tail -n1
:
In Version 5.1.1 wurde Unterstützung für das --block-size
-Flag:
Ich konnte jedoch keinen bestimmten Block extrahieren, da er keine vollständigen Header zwischen Blöcken enthält. Ich vermute, dass dies nicht von der Kommandozeile aus zu tun ist.
gzip
gzip
unterstützt auch die Verkettung. Ich habe (kurz) versucht, diesen Prozess für gzip
ohne Glück nachzuahmen. gzip --verbose --list
gibt nicht genügend Informationen und es scheint, dass die Header zu variabel sind, um sie zu finden.
Dies würde das Hinzufügen von Sync-Flushpoints erfordern, und da ihre Größe von der Größe des letzten Puffers in der vorherigen Komprimierung abhängt, ist dies in der Befehlszeile zu schwierig (benutze diczip oder ein anderes der zuvor diskutierten Tools) / p>
Ich habe apt-get install dictzip
gespielt und mit diczip gespielt, aber nur ein bisschen. Es funktioniert nicht ohne Argumente und erzeugt ein (massiv!)% Co_de% Archiv, das weder .dz
noch dictunzip
verstehen kann.
gunzip
bzip2
hat Header, die wir finden können. Das ist immer noch ein bisschen chaotisch, aber es funktioniert.
Dies ist genau wie bei der obigen Prozedur bzip2
:
Ich sollte beachten, dass dies erheblich langsamer ist als xz
(48 min für bzip2 vs 17 min für xz vs 1 min für xz
) sowie erheblich größer (97M für bzip2 vs 25M für xz -0
vs 15M für xz), zumindest für meine Testprotokolldatei.
Das ist etwas schwieriger, weil wir nicht den schönen Index haben. Wir müssen raten, wo wir hingehen müssen, und wir müssen auf der Seite des Scannens zu viel irren, aber mit einer riesigen Datei würden wir immer noch I / O speichern.
Meine Schätzung für diesen Test war 50000000 (aus dem ursprünglichen 52428800, eine pessimistische Schätzung, die für einen H.264-Film nicht pessimistisch genug ist.)
%Vor% Dies nimmt nur die letzten 50 Millionen Bytes, findet den binären Offset des letzten BZIP2-Headers, subtrahiert diesen von der Schätzwertgröße und zieht so viele Bytes vom Ende der Datei ab. Nur dieser Teil wird dekomprimiert und in xz -0
geworfen.
Da dies die komprimierte Datei zweimal abfragen muss und einen zusätzlichen Scan hat (der Aufruf tail
sucht den Header, der den gesamten erratenen Raum untersucht), ist dies eine suboptimale Lösung. Siehe auch den folgenden Abschnitt, wie langsam grep
wirklich ist.
Gegeben, wie schnell bzip2
ist, ist es leicht die beste Wette; Die schnellste Option ( xz
) ist ziemlich schnell zu komprimieren oder zu dekomprimieren und erstellt eine kleinere Datei als xz -0
oder gzip
in der Protokolldatei, mit der ich getestet habe. Andere Tests (sowie verschiedene online verfügbare Quellen) legen nahe, dass bzip2
in allen Szenarien xz -0
vorzuziehen ist.
Timing-Tests waren nicht umfassend, ich habe nichts gemittelt und Disk-Caching wurde verwendet. Trotzdem sehen sie richtig aus; Es gibt einen sehr geringen Overhead von bzip2
plus das Starten von 145 Komprimierungsinstanzen anstelle von nur einem (dies kann sogar ein Netto Gewinn sein, wenn ein anderes Multithread-Dienstprogramm mehrere Threads konsumieren kann) .