Angenommen, ich habe eine große Textdatei (> 2 GB) und möchte nur cat
die Zeilen X
an Y
(z. B. 57890000 bis 57890010).
Soweit ich weiß, kann ich dies durch Piping head
in tail
oder umgekehrt tun, d. H
head -A /path/to/file | tail -B
oder alternativ
tail -C /path/to/file | head -D
wobei A
, B
, C
und D
kann aus der Anzahl der Zeilen in der Datei berechnet werden, X
und Y
.
Bei diesem Ansatz gibt es jedoch zwei Probleme:
- Sie müssen berechnen
A
,B
,C
undD
. - Die Befehle könnten
pipe
sich viel mehr Zeilen , als ich bei der Lektüre interessiert bin (zB wenn ich nur ein paar Zeilen in der Mitte einer großen Datei lese)
Gibt es eine Möglichkeit, die Shell nur mit den gewünschten Zeilen arbeiten und diese ausgeben zu lassen? (während nur X
und Y
)?
tail
cat
large-files
head
Amelio Vazquez-Reina
quelle
quelle
Antworten:
Ich schlage die
sed
Lösung vor, aber der Vollständigkeit halberNach der letzten Zeile ausschneiden:
Geschwindigkeitstest:
seq 100000000 > test.in
real
zeit wie vonbash
's builtin gemeldettime
Dies sind keine präzisen Benchmarks, aber der Unterschied ist deutlich und wiederholbar genug, um einen guten Eindruck von der relativen Geschwindigkeit jedes dieser Befehle zu erhalten.
*: Außer zwischen den ersten beiden
sed -n p;q
undhead|tail
, die im Wesentlichen gleich zu sein scheinen.quelle
tail -n +50000000 test.in | head -n10
, was anderstail -n-50000000 test.in | head -n10
als das richtige Ergebnis geben würde?tail+|head
ist um 10-15% schneller als sed, ich habe diesen Benchmark hinzugefügt.-c
zum Überspringen von Zeichen verwenden,tail+|head
erfolgt dies augenblicklich. Natürlich können Sie nicht "50000000" sagen und müssen möglicherweise den Anfang des gesuchten Abschnitts manuell suchen.Wenn Sie die Zeilen X bis Y (beginnend mit 1) verwenden möchten, verwenden Sie
tail
liest und verwirft die ersten X-1-Zeilen (daran führt kein Weg vorbei), liest und druckt dann die folgenden Zeilen.head
liest und druckt die angeforderte Anzahl von Zeilen und beendet dann. Wennhead
Ausfahrten,tail
empfangen ein SIGPIPE Signal und sterben, so wird es nicht mehr als die Wert einer Puffergröße gelesen hat ( in der Regel ein paar Kilobyte) von Zeilen aus der Eingabedatei.Alternativ, wie von gorkypl vorgeschlagen, benutze sed:
Die sed-Lösung ist jedoch bedeutend langsamer (zumindest für GNU- und Busybox-Dienstprogramme; sed ist möglicherweise wettbewerbsfähiger, wenn Sie einen großen Teil der Datei auf einem Betriebssystem extrahieren, auf dem Piping langsam und sed schnell ist). Hier finden Sie schnelle Benchmarks unter Linux. Die Daten wurden von generiert
seq 100000000 >/tmp/a
, die Umgebung ist Linux / amd64,/tmp
ist tmpfs und der Computer ist ansonsten im Leerlauf und nicht austauschbar.Wenn Sie den Byte-Bereich kennen, mit dem Sie arbeiten möchten, können Sie ihn schneller extrahieren, indem Sie direkt zur Startposition springen. Aber für Zeilen muss man von Anfang an lesen und Zeilenumbrüche zählen. So extrahieren Sie Blöcke von x inclusive bis y exclusive ab 0 mit einer Blockgröße von b:
quelle
tail will read and discard the first X-1 line
scheint vermieden zu werden, wenn die Anzahl der Zeilen ab dem Ende angegeben wird. In diesem Fall scheint der Schwanz ab dem Ende entsprechend den Ausführungszeiten rückwärts zu lesen. Bitte lesen:http://unix.stackexchange.com/a/216614/79743
.tail
(einschließlich GNU-Tail) Heuristiken, die am Ende gelesen werden können. Das verbessert dietail | head
Lösung im Vergleich zu anderen Methoden.Der
head | tail
Ansatz ist einer der besten und "idiomatischsten" Wege, dies zu tun:Ein schnellerer Weg ist, wie Gilles in den Kommentaren hervorhob
Der Grund dafür ist, dass die ersten X - 1 - Linien im Vergleich zum
head | tail
Anflug nicht durch die Leitung gehen müssen .Ihre formulierte Frage ist etwas irreführend und erklärt wahrscheinlich einige Ihrer unbegründeten Bedenken in Bezug auf diesen Ansatz.
Sie sagen , Sie berechnen müssen
A
,B
,C
,D
aber wie Sie sehen können, die Zeilenzahl der Datei nicht benötigt wird und höchstens 1 Berechnung notwendig, was die Shell sowieso für Sie tun können.Sie befürchten, dass die Rohrleitungen mehr Zeilen als nötig lesen. In der Tat ist dies nicht wahr: Es
tail | head
ist ungefähr so effizient, wie Sie es in Bezug auf Datei-E / A bekommen können. Betrachten Sie zunächst den minimalen Arbeitsaufwand: Um die X -te Zeile in einer Datei zu finden, müssen Sie im Allgemeinen jedes Byte lesen und anhalten, wenn Sie X Zeilenumbruchsymbole zählen, da es keine Möglichkeit gibt, die Datei zu teilen Versatz der X -ten Linie. Sobald Sie die * X * -te Zeile erreicht haben, müssen Sie alle Zeilen lesen, um sie zu drucken. Halten Sie an der Y -ten Zeile an. Daher kommt kein Ansatz durch, wenn weniger als Y- Zeilen gelesen werden.head -n $Y
Liest jetzt nicht mehr als YZeilen (auf die nächste Puffereinheit gerundet, aber bei korrekter Verwendung verbessern Puffer die Leistung, sodass Sie sich keine Gedanken über diesen Overhead machen müssen). Darüber hinaustail
wird nicht mehr als gelesenhead
, so dass wir gezeigt haben, dasshead | tail
die geringstmögliche Anzahl von Zeilen gelesen wird (wieder plus einige vernachlässigbare Puffer, die wir ignorieren). Der einzige Effizienzvorteil eines einzigen Tool-Ansatzes, bei dem keine Pipes verwendet werden, sind weniger Prozesse (und damit weniger Overhead).quelle
Der orthodoxste Weg (aber nicht der schnellste, wie Gilles oben bemerkt hat ) wäre zu benutzen
sed
.In deinem Fall:
Die
-n
Option impliziert, dass nur die relevanten Zeilen auf stdout gedruckt werden.Das p am Ende der Zielliniennummer bedeutet, dass Linien in einem bestimmten Bereich gedruckt werden. Das q im zweiten Teil des Skripts spart Zeit, indem der Rest der Datei übersprungen wird.
quelle
sed
undtail | head
etwa auf dem Niveau zu sein, aber es stellt sich heraus , dasstail | head
ist deutlich schneller (siehe meine Antwort ).tail
/head
sind mehr „orthodox“ betrachtet, da Trimmen entweder Ende einer Datei ist genau das, was sie gemacht für. In diesen Materialiensed
scheint es nur dann ins Bild zu kommen, wenn Substitutionen erforderlich sind - und schnell aus dem Bild gestoßen zu werden, wenn etwas viel Komplexeres passiert, da die Syntax für komplexe Aufgaben so viel schlechter ist als die von AWK, die dann übernimmt .Wenn wir den auszuwählenden Bereich kennen, können wir von der ersten
lStart
bis zur letzten ZeilelEnd
Folgendes berechnen:Wenn wir die Gesamtzahl der Zeilen kennen, können
lAll
wir auch die Entfernung zum Ende der Datei berechnen:Dann werden wir beide kennen:
Die kleinste davon auswählen:
tailnumber
wie folgt:Ermöglicht die Verwendung des durchweg schnellsten Ausführungsbefehls:
Bitte beachten Sie das zusätzliche Pluszeichen ("+"), wenn
$linestart
ausgewählt.Die einzige Einschränkung besteht darin, dass wir die Gesamtzahl der Zeilen benötigen. Das Auffinden kann einige zusätzliche Zeit in Anspruch nehmen.
Wie üblich mit:
Einige Male gemessen sind:
Beachten Sie, dass sich die Zeiten drastisch ändern, wenn sich die ausgewählten Linien in der Nähe des Starts oder des Endes befinden. Ein Befehl, der auf einer Seite der Datei gut zu funktionieren scheint, kann auf der anderen Seite der Datei sehr langsam sein.
quelle
Ich mache das oft genug und habe dieses Skript geschrieben. Ich muss die Zeilennummern nicht finden, das Skript erledigt alles.
quelle
tail|head
, was in der Frage und den anderen Antworten ausführlich besprochen wurde, und 90% bestimmen die Zeilennummern, in denen bestimmte Zeichenfolgen / Muster erscheinen, die nicht Teil der Frage waren . PS Sie sollten immer Ihre Shell-Parameter und -Variablen angeben; zB "$ 3" und "$ 4".