Якщо я хочу до tail
25 Гб текстового файлу, чи tail
читає команда весь файл?
Оскільки файл може бути розкиданий на диску, я думаю, що це має бути, але я не розумію таких внутрішніх справ.
Якщо я хочу до tail
25 Гб текстового файлу, чи tail
читає команда весь файл?
Оскільки файл може бути розкиданий на диску, я думаю, що це має бути, але я не розумію таких внутрішніх справ.
Відповіді:
Ні, tail
не читає весь файл, він прагне до кінця, потім зчитує блоки назад, поки не буде досягнуто очікуваної кількості рядків, потім він відображає рядки у правильному напрямку до кінця файлу і, можливо, залишається за моніторингом файл, якщо використовується -f
параметр.
Однак зауважте, що tail
немає іншого вибору, окрім того, як прочитати цілі дані, якщо вони надані не шукаючими введеннями, наприклад, під час читання з труби.
Аналогічно, коли запитують шукати рядки, починаючи з початку файлу, використовуючи tail -n +linenumber
синтаксис або tail +linenumber
нестандартний параметр при підтримці, tail
явно читає весь файл (якщо не переривається).
tail +n
буде прочитаний весь файл - спочатку знайти потрібну кількість нових рядків, а потім вивести решту.
tail
роблять це чи роблять правильно. Наприклад, tail
в цьому відношенні розбита область 1,21,1. Також зауважте, що поведінка змінюється, коли tail
ing stdin, і де stdin є звичайним файлом, а початкове положення у файлі не на початку, коли tail
викликається (як у { cat > /dev/null; tail; } < file
)
Ви могли бачити, як tail
працює сам. Як ви можете, для одного з моїх файлів read
робиться три рази і загалом читається приблизно 10 К байт:
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
показує, що системні дзвінки tail
роблять під час запуску. Деякі відомості про системні дзвінки ви можете прочитати тут en.wikipedia.org/wiki/System_call . Коротко - відкрити - відкриває файл і повертає ручку (3 у цьому прикладі), lseek
позиції, де ви збираєтесь читати, і read
просто читає, і як ви бачите, повертає, скільки байтів прочитано,
Оскільки файл може бути розкиданий на диску, я думаю, що він повинен [читати файл послідовно], але я не розумію таких внутрішніх справ.
Як ви тепер знаєте, він tail
просто прагне до кінця файлу (із системним викликом lseek
) і працює назад. Але у цитуваному вище зауваженні вам цікаво, "як хвіст знає, де на диску знайти кінець файлу?"
Відповідь проста: Хвіст не знає. Процеси на рівні користувача бачать файли як безперервні потоки, тому всі tail
можуть знати, що це зсув від початку файлу. Але у файловій системі "inode" (запис каталогу) файлу пов'язаний зі списком чисел, що позначають фізичне розташування блоків даних файлу. Коли ви читаєте з файлу, ядро / драйвер пристрою визначає, яка саме частина вам потрібна, визначає його розташування на диску і вибирає його для вас.
Це те, з чим ми маємо операційні системи: тому вам не потрібно турбуватися про те, куди розкидаються блоки вашого файлу.
Якщо head
чи tail
здається, що він читає весь файл, ймовірною причиною є те, що файл містить мало символів нового рядка або їх немає . Я зіткнувся з цим кілька місяців тому дуже великим (гігабайтним) блоком JSON, який був серіалізований без пробілу, навіть не в рядках.
Якщо у вас є голова / хвіст GNU, ви можете використовувати -c N
для друку першого / останнього N байт замість рядків , але, на жаль, це не є функцією POSIX.
Як ви бачите у рядку 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. */