Nếu tôi muốn tail
một tail
tệp văn bản 25 GB, lệnh có đọc toàn bộ tệp không?
Vì một tập tin có thể nằm rải rác trên đĩa, tôi tưởng tượng nó phải như vậy, nhưng tôi không hiểu rõ về nội bộ như vậy.
Nếu tôi muốn tail
một tail
tệp văn bản 25 GB, lệnh có đọc toàn bộ tệp không?
Vì một tập tin có thể nằm rải rác trên đĩa, tôi tưởng tượng nó phải như vậy, nhưng tôi không hiểu rõ về nội bộ như vậy.
Câu trả lời:
Không, tail
không đọc toàn bộ tệp, nó tìm đến cuối rồi đọc ngược khối cho đến khi đạt được số dòng dự kiến, sau đó nó hiển thị các dòng theo hướng thích hợp cho đến khi kết thúc tệp và có thể vẫn theo dõi tập tin nếu -f
tùy chọn được sử dụng.
Tuy nhiên, lưu ý rằng tail
không có lựa chọn nào khác ngoài việc đọc toàn bộ dữ liệu nếu được cung cấp đầu vào không thể tìm kiếm, ví dụ như khi đọc từ một đường ống.
Tương tự, khi được yêu cầu tìm kiếm các dòng bắt đầu từ đầu tệp, với việc sử dụng tail -n +linenumber
cú pháp hoặc tail +linenumber
tùy chọn không chuẩn khi được hỗ trợ, tail
rõ ràng sẽ đọc toàn bộ tệp (trừ khi bị gián đoạn).
tail +n
sẽ đọc toàn bộ tệp - đầu tiên để tìm số dòng mới mong muốn, sau đó xuất ra phần còn lại.
tail
thực hiện làm điều đó hoặc làm điều đó đúng. Ví dụ, busybox 1.21.1 tail
bị hỏng trong vấn đề đó. Cũng lưu ý rằng hành vi thay đổi khi tail
ing stdin và trong đó stdin là tệp thông thường và vị trí ban đầu trong tệp không ở đầu khi tail
được gọi (như trong { cat > /dev/null; tail; } < file
)
Bạn có thể thấy cách làm tail
việc của mình. Như bạn có thể cho một trong các tệp của tôi read
được thực hiện ba lần và tổng cộng khoảng 10K byte được đọc:
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
hiển thị những gì hệ thống gọi tail
làm khi chạy. Một số giới thiệu về các cuộc gọi hệ thống mà bạn có thể đọc ở đây en.wikipedia.org/wiki/System_call . Ngắn gọn - mở - mở một tệp và trả về một tay cầm (3 trong ví dụ này), lseek
các vị trí bạn sẽ đọc và read
chỉ đọc và như bạn có thể thấy nó trả về số lượng byte được đọc,
Vì một tệp có thể nằm rải rác trên đĩa, tôi tưởng tượng nó phải [đọc tệp liên tục], nhưng tôi không hiểu rõ nội bộ như vậy.
Như bạn đã biết, tail
chỉ cần tìm đến cuối tệp (với lệnh gọi hệ thống lseek
) và làm việc ngược lại. Nhưng trong phần nhận xét được trích dẫn ở trên, bạn đang tự hỏi "làm thế nào để đuôi biết nơi nào trên đĩa để tìm phần cuối của tệp?"
Câu trả lời rất đơn giản: Đuôi không biết. Các quy trình ở cấp độ người dùng xem các tệp dưới dạng luồng liên tục, vì vậy tất cả tail
có thể biết là phần bù từ đầu tệp. Nhưng trong hệ thống tệp, "inode" của tệp (mục nhập thư mục) được liên kết với một danh sách các số biểu thị vị trí thực của các khối dữ liệu của tệp. Khi bạn đọc từ tệp, kernel / trình điều khiển thiết bị sẽ tìm ra phần bạn cần, tìm ra vị trí của nó trên đĩa và tìm nạp nó cho bạn.
Đó là loại điều chúng tôi có hệ điều hành: vì vậy bạn không phải lo lắng về việc các khối tệp của bạn nằm rải rác ở đâu.
Nếu head
hoặc tail
dường như đang đọc toàn bộ tệp, một lý do có thể là tệp chứa ít hoặc không có ký tự dòng mới . Tôi đã vấp phải điều này một vài tháng trước với một blob JSON (gigabyte) rất lớn đã được tuần tự hóa mà không có khoảng trắng nào, thậm chí không có trong chuỗi.
Nếu bạn có đầu / đuôi GNU, bạn có thể sử dụng -c N
để in N byte đầu tiên / cuối cùng thay vì các dòng , nhưng thật không may, đây không phải là một tính năng POSIX.
Như bạn có thể thấy trong dòng mã nguồn 525, bạn có thể thấy các ý kiến cho việc thực hiện.
/* 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. */