Wenn Ihre Segmente nicht wirklich riesig sind (wie in: Sie können wirklich nicht so viel RAM sparen, vermutlich weil dies ein winziges eingebettetes System ist, das ein großes Dateisystem steuert), ist ein einziger Durchgang wirklich der bessere Ansatz. Nicht nur, weil es schneller sein wird, sondern vor allem, weil die Quelle ein Stream sein kann, aus dem alle gelesenen und nicht gespeicherten Daten verloren gehen. Dies ist wirklich ein Job für awk, obwohl sed es auch kann.
sed -n -e 's/^---$//' -e 't a' \
-e 'H' -e '$g' -e '$s/^\n//' -e '$p' -e 'b' \
-e ':a' -e 'h' # you are not expected to understand this
awk '{if (/^---$/) {chunk=""} # separator ==> start new chunk
else {chunk=chunk $0 RS}} # append line to chunk
END {printf "%s", chunk}' # print last chunk (without adding a newline)
Wenn Sie einen Zwei-Pass-Ansatz verwenden müssen, bestimmen Sie den Zeilenversatz des letzten Trennzeichens und drucken Sie daraus. Oder bestimmen Sie den Byte-Offset und drucken Sie daraus.
</input/file tail -n +$((1 + $(</input/file # print the last N lines, where N=…
grep -n -e '---' | # list separator line numbers
tail -n 1 | # take the last one
cut -d ':' -f 1) )) # retain only line number
</input/file tail -n +$(</input/file awk '/^---$/ {n=NR+1} END {print n}')
</input/file tail -c +$(</input/file LC_CTYPE=C awk '
{pos+=length($0 RS)} # pos contains the current byte offset in the file
/^---$/ {last=pos} # last contains the byte offset after the last separator
END {print last+1} # print characters from last (+1 because tail counts from 1)
')
Nachtrag: Wenn Sie mehr als POSIX haben, finden Sie hier eine einfache One-Pass-Version, die auf einer gemeinsamen Erweiterung von awk basiert, mit der das Datensatztrennzeichen RS
ein regulärer Ausdruck sein kann (POSIX lässt nur ein einziges Zeichen zu). Es ist nicht ganz richtig: Wenn die Datei mit einem Datensatztrennzeichen endet, wird der Block vor dem letzten Datensatztrennzeichen anstelle eines leeren Datensatzes gedruckt. Die zweite Version, die verwendet wird, RT
vermeidet diesen Fehler, RT
ist jedoch spezifisch für GNU awk.
awk -vRS='(^|\n)---+($|\n)' 'END{printf $0}'
gawk -vRS='(^|\n)---+($|\n)' 'END{if (RT == "") printf $0}'
Gilles 'SO - hör auf böse zu sein'
quelle
sed
funktioniert gut, aber ich kann dasawk
Beispiel nicht zum Laufen bringen; es hängt ... und ich erhalte eine Fehlermeldung im 3. Beispiel:cut -f ':' -t 1
... cut: ungültige Option - 't'cut
Beispiel durcheinander gebracht . Ich sehe nichts Falsches an demawk
Beispiel, welche Version von awk verwenden Sie und was ist Ihre Testeingabe?awk
funktioniert die Version .. es dauert nur sehr lange bei einer großen Datei .. diesed
Version hat dieselbe Datei in 0.470s verarbeitet .. Meine Testdaten sind sehr gewichtet ... nur zwei Chunks mit einem einsamen '---' drei Zeilen vom Ende von 1 Million Zeilen ...Eine Zwei-Pass-Strategie scheint das Richtige zu sein. Anstelle von sed würde ich verwenden
awk(1)
. Die beiden Pässe könnten so aussehen:um die Zeilennummer zu erhalten. Und dann den gesamten Text ab dieser Zeilennummer wiedergeben mit:
Dies sollte keine übermäßige Pufferung erfordern.
quelle
awk -v line=$(awk '/^---$/{n=NR}END{print n}' file) 'NR>line' file
Die erste
sed
Ausgabe gibt die Zeilennummern der "---" Zeilen aus ...Die zweite
sed
extrahiert die letzte Nummer aus der Ausgabe des ersten Sed ...Addieren Sie 1 zu dieser Nummer, um den Start Ihres "ccc" -Blocks zu erhalten ...
Die dritte 'sed' gibt vom Anfang des "ccc" -Blocks an EOF aus
Update (mit geänderten Informationen zu Gilles-Methoden)
Nun, ich habe darüber nachgedacht, wie sich Glenn Jackmans verhalten
tac
würde, also habe ich die drei Antworten (zum Zeitpunkt des Schreibens) zeitgetestet ... Die Testdatei (en) enthielten jeweils 1 Million Zeilen (mit ihren eigenen Zeilennummern).Alle Antworten haben das getan, was erwartet wurde ...
Hier sind die Zeiten ..
Gilles
sed
(Einzelpass)Gilles
awk
(Einzelpass)Gilles 'Zwei-Pass' (erste Methode)
Gilles 'Two-Pass' (zweite Methode) ... sehr schnell
Gilles 'Zwei-Pass' (dritte Methode)
Gilles 'gawk' (RT-Methode) ... sehr schnell , aber nicht POSIX.
Glenn Jackman ... sehr schnell , ist aber nicht POSIX.
fred.bear
Mackie Messer
quelle
Verwenden Sie " tac ", das die Zeilen einer Datei von Ende bis Anfang ausgibt:
quelle
tac
ist nicht POSIX, sondern Linux-spezifisch (in GNU-Coreutils und in einigen Busybox-Installationen).Sie könnten einfach verwenden
ed
So funktioniert es:
t
dupliziert die aktuelle (.
) Zeile - die beimed
Start immer die letzte Zeile ist (nur für den Fall, dass das Trennzeichen in der letzten Zeile vorhanden ist),1,?===?d
löscht alle Zeilen bis einschließlich der vorherigen Übereinstimmung (ed
befindet sich noch in der letzten Zeile) )$d
löscht dann die (doppelte) letzte Zeile,,p
druckt den Textpuffer (ersetzen durchw
, um die Datei an Ort und Stelle zu bearbeiten) und wird schließlichq
beendeted
.Wenn Sie wissen, dass die Eingabe mindestens ein Trennzeichen enthält (und es Ihnen egal ist, ob es auch gedruckt wird), dann
wäre das kürzere.
So funktioniert es: Es hängt alle Zeilen an den
H
alten Puffer an, überschreibt denh
alten Puffer, wenn eine Übereinstimmung auftritt, löschtd
alle Zeilen außer der letzten,$
wenn esx
die Puffer (und Autoprints) wechselt.quelle