Ist es in Bash möglich, das Lesen einer Datei von einem beliebigen Byte-Zähler-Offset aus zu starten?

21

Ich möchte ein Datum ausfindig machen, das sich irgendwo in einem 8-GB-Protokoll befindet (Text).

Kann ich einen vollständigen sequentiellen Lesevorgang etwas umgehen und zuerst binäre Teilungen der Datei (Größe) oder irgendwie im Dateisystem navigieren inodes(über das ich nur sehr wenig weiß ), um an jedem Teilungspunkt mit dem Lesen zu beginnen, bis ich einen geeigneten Versatz von gefunden habe? Wo soll ich mit der Textsuche nach einer Zeile beginnen, die das Datum enthält?

tailBeim Lesen der letzten Zeile wird kein normales sequentielles Lesen verwendet. Daher frage ich mich, ob diese Funktion in Bash verfügbar ist oder ob ich Python oder C / C ++ verwenden muss. Ich bin jedoch speziell an einer bashOption interessiert ..

Peter.O
quelle

Antworten:

8
for (( block = 0; block < 16; block += 1 ))
do 
    echo $block; 
    dd if=INPUTFILE skip=$((block*512))MB bs=64 count=1 status=noxfer 2> /dev/null | \
        head -n 1
done

Das Programm .. erstellt keine temporären Split-Dateien, überspringt Blöcke mit 512 MB Daten bei jedem Durchlauf, liest 64 Byte von dieser Position und begrenzt die Ausgabe auf die erste Zeile dieser 64 Byte.

Vielleicht möchten Sie 64 auf das einstellen, was Sie für nötig halten.

Akira
quelle
@akira .. Das sieht wirklich gut aus, aber ich möchte es zuerst etwas genauer betrachten .. (also bis morgen .....
Peter.O
1
@akira .. "dd" ist super. Es funktioniert gut mit der binären Split-Suche ... Ich kann jetzt eine reguläre Zeile (nach Datum) aus einer sortierten 8G-Datei in weniger als 1 Sekunde extrahieren ... Es sieht also so aus, als würde ich meine 3 erreichen zweites persönliches Ziel zum Extrahieren einer Reihe von Datumsangaben zwischen zwei Schlüsseln (einschließlich). Ohne die Ausgabezeit, die abhängig davon ist, wie viel ausgegeben wird. Ich werde sie auch dafür verwenden. ddEs ist ein großartiges Werkzeug! :)
Peter.O
29

Es klingt wie Sie wollen:

tail -c +1048576

oder wie viele Bytes Sie überspringen möchten. Das Pluszeichen weist den Tail an, vom Anfang der Datei anstatt vom Ende zu messen. Wenn Sie die GNU-Version von tail verwenden, können Sie dies wie folgt schreiben:

tail -c +1M

Um eine feste Anzahl von Bytes nach dem Ausschneiden zu erhalten, leiten Sie diese anstelle des gesamten Restes der Datei einfach durch den Kopf:

tail -c +1048576 | head -c 1024
Ross Smith
quelle
Die Flexibilität von Linux / Bash ist überwältigend (ich habe definitiv zu lange auf Linux gewechselt). Ich hatte gerade Akiras Antwort akzeptiert, aber ich habe sie gezogen, bis ich sie genauer eingeschätzt habe. ddspringt zu einem bestimmten Byte (wie auch tail), aber es ist ein Schmerz, der unbekannte Zeilenlängen umkodiert, und dann ein Aufruf an sed, um führende Teilzeilen abzulösen ... Es sieht so aus, als ob tail | head dies schmerzlos tun kann (so schnell?) . Ich verstehe nicht, wie der Kopf den Hahn am Schwanz abstellen kann, aber es scheint :) Es muss der Fall sein: Wenn der Kopf aufhört zu empfangen, hört der Schwanz auf zu senden (und hört auf weiter zu lesen). Muss gehen .. morgen zurück.
Peter.O
@ fred.bear: tail/ headsind nicht in der Lage , die Leitungslängen als auch blind erraten. Sie müssen zu Position x springen und dann können Sie entweder links oder rechts von x nach dem nächsten suchen \n. es ist egal, wie das Programm heißt. In beiden Fällen springen Sie also zu x und headsuchen dann mit nach rechts das nächste Zeilenende.
Akira
tail|headbietet die Möglichkeit, sich überhaupt nicht um dd's count = val zu kümmern . Wenn ich mit 'dd' nicht genügend Daten erhalte, ist das Spiel zu Ende. Die Flexibilität beliebiger Leitungslängen ist groß. Ich habe eine Funktion für 'dd' geschrieben, die die "nächstgelegene" vollständige Zeile und ihren Versatz zurückgibt, aber ich würde es vorziehen, das Längenproblem zu vermeiden. Ich habe jetzt tail | head getestet und es funktioniert anfangs gut (auf Offset = 100 MB), verlangsamt sich aber dramatisch, um 2 Minuten für einen Zugriff auf Offset = 8 GB zu benötigen (ich kann awkes in 1 Minute) ... also ist es großartig für kleinere Dateien .. Danke, dass du mich auf die Schwanz / Kopf-Kombination aufmerksam gemacht hast :)
Peter.O
2

Ich würde so etwas versuchen, um das Protokoll zur schnelleren Analyse in 512-MB-Blöcke aufzuteilen.

split <filename> -b 536870912

Wenn Sie nach der Datei suchen, funktioniert Folgendes:

for file in x* ; do
  echo $file
  head -n 1 $file
done

Verwenden Sie diese Ausgabe, um zu bestimmen, welche Datei für Ihr Datum verwendet werden soll.

Sifusam
quelle
Danke, aber es ist langsamer als eine sequentielle Suche. Werfen Sie einen Blick auf meine Kommentare hier unix.stackexchange.com/questions/8121/… (anstatt dasselbe hier neu zu schreiben)
Peter.O
Mit 'split' berühren Sie jedes einzelne Byte einmal. wenn du das tust, könntest du auch die ganzen 8 GB mitbekommen.
Akira
@sifusam .. Ich möchte eine binäre Split - Suche tun ( und nicht nur die Dateien aufgeteilt) en.wikipedia.org/wiki/Binary_search_algorithm ... so ist es eine gute Antwort für eine differnt Frage :) .. Vielen Dank für die Beantwortung war .. +1, um Sie ins Rollen zu bringen ....
Peter.O
0

Hier ist mein Skript, ich suche die erste Zeile, in der das erste Feld meiner Nummer entspricht. Die Zeilen sind nach dem ersten Feld sortiert. Ich benutze dd, um die erste Zeile von 128K-Blöcken zu überprüfen, dann springe ich zum Block und führe eine Suche durch. Es verbessert die Effizienz, wenn die Datei mehr als 1 MB groß ist.

Jeder Kommentar oder jede Korrektur ist willkommen!

#!/bin/bash

search=$1;
f=$2;

bs=128;

max=$( echo $(du $f | cut -f1)" / $bs" | bc );
block=$max;
for i in $(seq 0 $max); do
 n=$(dd bs=${bs}K skip=$i if=$f 2> /dev/null| head -2 | tail -1 | cut -f1)
 if [ $n -gt $search ]; then
  block=`expr $i - 1` 
  break;
 fi
done; 
dd bs=${bs}K skip=$block if=$f 2> /dev/null| tail -n +2 | awk -v search="$search" '$1==search{print;exit 1;};$1>search{exit 1;};';

* EDIT * ** grep ist viel schneller und ack noch besser

user59892
quelle