Nếu tôi muốn tailmột tailtệ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 tailmột tailtệ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, tailkhô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 -ftùy chọn được sử dụng.
Tuy nhiên, lưu ý rằng tailkhô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 +linenumbercú pháp hoặc tail +linenumbertùy chọn không chuẩn khi được hỗ trợ, tailrõ ràng sẽ đọc toàn bộ tệp (trừ khi bị gián đoạn).
tail +nsẽ đọ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.
tailthực hiện làm điều đó hoặc làm điều đó đúng. Ví dụ, busybox 1.21.1 tailbị hỏng trong vấn đề đó. Cũng lưu ý rằng hành vi thay đổi khi tailing 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 tailviệ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
stracehiển thị những gì hệ thống gọi taillà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), lseekcác vị trí bạn sẽ đọc và readchỉ đọ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, tailchỉ 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ả tailcó 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 headhoặ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. */