25 GBのテキストファイルをtail
したい場合、tail
コマンドはファイル全体を読み取りますか?
ファイルはディスク上に散らばっているかもしれないので、そうする必要があると思いますが、そのような内部についてはよくわかりません。
いいえ、tail
はファイル全体を読み取るのではなく、最後までシークし、予想される行数に達するまでブロックを逆方向に読み取り、ファイルの終わりまで適切な方向に行を表示します、および-f
オプションが使用されている場合は、ファイルの監視を継続します。
ただし、パイプから読み取る場合など、シークできない入力が提供された場合、tail
にはデータ全体を読み取る以外に選択肢がないことに注意してください。
同様に、tail -n +linenumber
構文またはtail +linenumber
非標準オプションがサポートされている場合にtail
を使用すると、ファイルの先頭から始まる行を検索するように求められた場合、ファイル全体を読み取ります(ただし、中断された)。
tail
がどのように機能するかを確認できたでしょう。私のファイルの1つでできるように、read
は3回行われ、合計でおよそ10Kバイトが読み取られます。
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
ファイルがディスク上に散在している可能性があるので、[ファイルを順番に読み取る]必要があると思いますが、そのような内部についてはよくわかりません。
ご存じのように、tail
はファイルの終わりまでシークし(システムコールlseek
を使用)、逆方向に動作します。しかし、上記のコメントでは、"tailがディスク上のどこでファイルの終わりを見つけるのかをどのように知るのですか?"
答えは簡単です。テールは知りません。ユーザーレベルのプロセスは、ファイルを連続したストリームとして認識しているため、tail
が認識できるのは、ファイルの先頭からのオフセットだけです。しかし、ファイルシステムでは、ファイルの「iノード」(ディレクトリエントリ)は、ファイルのデータブロックの物理的な場所を示す番号のリストに関連付けられています。ファイルから読み取ると、カーネル/デバイスドライバーは必要な部分を見つけ出し、ディスク上のその場所を計算して、それをフェッチします。
これは、オペレーティングシステムの一種です。そのため、ファイルのブロックがどこに分散しているかについて心配する必要はありません。
head
またはtail
appearsがファイル全体を読み取る場合、考えられる理由はファイルに含まれる改行文字がほとんどまたはまったくないであると考えられます。数か月前に、文字列でさえも空白を一切含まずにシリアル化された非常に大きな(ギガバイト)JSON blobを使用して、これを試しました。
GNU head/tailがある場合、-c N
最初の/最後のN bytesをlinesの代わりに出力しますが、残念ながらこれはPOSIX機能ではありません。
source code 525行目を見るとわかるように、実装のコメントを見ることができます。
/* 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. */