Wenn ich tail
eine 25 GB große Textdatei haben möchte, liest der tail
Befehl dann die gesamte Datei?
Da eine Datei auf einer Festplatte verstreut sein könnte, stelle ich mir das vor, aber ich verstehe solche Interna nicht gut.
Wenn ich tail
eine 25 GB große Textdatei haben möchte, liest der tail
Befehl dann die gesamte Datei?
Da eine Datei auf einer Festplatte verstreut sein könnte, stelle ich mir das vor, aber ich verstehe solche Interna nicht gut.
Antworten:
Nein, tail
es wird nicht die gesamte Datei gelesen, es wird bis zum Ende gesucht und dann werden die Blöcke rückwärts gelesen, bis die erwartete Anzahl von Zeilen erreicht wurde. Anschließend werden die Zeilen in der richtigen Richtung bis zum Ende der Datei angezeigt und möglicherweise wird die Datei weiterhin überwacht Datei, wenn die -f
Option verwendet wird.
Beachten Sie jedoch, dass tail
es keine andere Wahl gibt, als die gesamten Daten zu lesen, wenn eine nicht suchbare Eingabe bereitgestellt wird, beispielsweise beim Lesen aus einer Pipe.
Wenn Sie aufgefordert werden, nach Zeilen zu suchen, die am Anfang der Datei beginnen, wird bei Verwendung der tail -n +linenumber
Syntax- oder tail +linenumber
Nicht-Standardoption tail
(sofern unterstützt) offensichtlich die gesamte Datei gelesen (sofern nicht unterbrochen).
tail +n
wird die gesamte Datei gelesen - zuerst, um die gewünschte Anzahl von Zeilenumbrüchen zu finden, dann, um den Rest auszugeben.
tail
Implementierungen dies tun oder ordnungsgemäß ausführen. Zum Beispiel ist busybox 1.21.1 tail
in dieser Hinsicht defekt. Beachten Sie auch, dass sich das Verhalten ändert, wenn tail
stdin und stdin eine reguläre Datei ist und die anfängliche Position in der Datei nicht am Anfang steht, wenn tail
aufgerufen wird (wie in { cat > /dev/null; tail; } < file
)
Du hättest sehen können, wie es tail
funktioniert. Wie Sie für eine meiner Dateien können, read
wird dreimal ausgeführt und insgesamt werden ungefähr 10 KByte gelesen:
strace 2>&1 tail ./huge-file >/dev/null | grep -e "read" -e "lseek" -e "open" -e "close"
open("./huge-file", O_RDONLY) = 3
lseek(3, 0, SEEK_CUR) = 0
lseek(3, 0, SEEK_END) = 80552644
lseek(3, 80551936, SEEK_SET) = 80551936
read(3, ""..., 708) = 708
lseek(3, 80543744, SEEK_SET) = 80543744
read(3, ""..., 8192) = 8192
read(3, ""..., 708) = 708
close(3) = 0
strace
Zeigt an, was Systemaufrufe beim Ausführen tail
tun. Einige Informationen zu Systemaufrufen finden Sie hier en.wikipedia.org/wiki/System_call . Kurz - öffnen - öffnet eine Datei und gibt ein Handle zurück (3 in diesem Beispiel), lseek
Positionen, an denen Sie lesen und read
gerade lesen werden, und wie Sie sehen können, gibt es zurück, wie viele Bytes gelesen werden,
Da eine Datei möglicherweise auf einer Festplatte verstreut ist, muss sie nacheinander gelesen werden, aber ich verstehe solche Interna nicht gut.
Wie Sie jetzt wissen, tail
sucht nur das Ende der Datei (mit dem Systemaufruf lseek
) und arbeitet rückwärts. Aber in der oben zitierten Bemerkung fragen Sie sich: "Woher weiß Tail, wo auf der Festplatte das Ende der Datei zu finden ist?"
Die Antwort ist einfach: Schwanz weiß es nicht. Bei Prozessen auf Benutzerebene werden Dateien als kontinuierliche Streams betrachtet, sodass tail
nur der Versatz vom Dateianfang bekannt ist. Im Dateisystem ist der "Inode" (Verzeichniseintrag) der Datei jedoch mit einer Liste von Zahlen verknüpft, die den physischen Speicherort der Datenblöcke der Datei angeben. Wenn Sie aus der Datei lesen, ermittelt der Kernel / der Gerätetreiber, welches Teil Sie benötigen, ermittelt seinen Speicherort auf der Festplatte und holt es für Sie.
Dafür haben wir Betriebssysteme: Sie müssen sich also keine Sorgen machen, wo die Blöcke Ihrer Datei verstreut sind.
Wenn head
oder tail
scheint , die gesamte Datei zu lesen, ist ein wahrscheinlicher Grund, dass die Datei wenige oder keine Zeilenumbrüche enthält . Ich bin vor ein paar Monaten darüber gestolpert, mit einem sehr großen (Gigabyte) JSON-Blob, der mit keinerlei Leerzeichen serialisiert wurde, nicht einmal in Strings.
Wenn Sie GNU head / tail haben, können Sie -c N
die ersten / letzten N Bytes anstelle von Zeilen ausgeben, dies ist jedoch leider keine POSIX-Funktion.
Wie Sie in der Quelltextzeile 525 sehen können, sehen Sie die Kommentare zur Implementierung.
/* Print the last N_LINES lines from the end of file FD.
Go backward through the file, reading 'BUFSIZ' bytes at a time (except
probably the first), until we hit the start of the file or have
read NUMBER newlines.
START_POS is the starting position of the read pointer for the file
associated with FD (may be nonzero).
END_POS is the file offset of EOF (one larger than offset of last byte).
Return true if successful. */