Чи хвостик читає весь файл?


113

Якщо я хочу до tail25 Гб текстового файлу, чи tailчитає команда весь файл?

Оскільки файл може бути розкиданий на диску, я думаю, що це має бути, але я не розумію таких внутрішніх справ.

Відповіді:


119

Ні, tailне читає весь файл, він прагне до кінця, потім зчитує блоки назад, поки не буде досягнуто очікуваної кількості рядків, потім він відображає рядки у правильному напрямку до кінця файлу і, можливо, залишається за моніторингом файл, якщо використовується -fпараметр.

Однак зауважте, що tailнемає іншого вибору, окрім того, як прочитати цілі дані, якщо вони надані не шукаючими введеннями, наприклад, під час читання з труби.

Аналогічно, коли запитують шукати рядки, починаючи з початку файлу, використовуючи tail -n +linenumberсинтаксис або tail +linenumberнестандартний параметр при підтримці, tailявно читає весь файл (якщо не переривається).


14
Чорт, занадто швидко :-). Ось відповідний вихідний код . Друкуйте останні N_LINES рядки з кінця файлу FD. Пройдіться назад по файлу, читаючи байти "BUFSIZ" одночасно (крім, мабуть, першого), поки ми не натиснемо на початок файлу чи не прочитаємо НОМЕРА нових рядків.
— Патрік

1
Також tail +nбуде прочитаний весь файл - спочатку знайти потрібну кількість нових рядків, а потім вивести решту.
— СФ.

@SF. Дійсно, відповідь оновлена.
— jlliagre

4
Зауважте, що не всі програми tailроблять це чи роблять правильно. Наприклад, tailв цьому відношенні розбита область 1,21,1. Також зауважте, що поведінка змінюється, коли tailing stdin, і де stdin є звичайним файлом, а початкове положення у файлі не на початку, коли tailвикликається (як у { cat > /dev/null; tail; } < file)
— Stéphane Chazelas

4
@StephaneChazelas * nix - світ дивних крайових випадків стає нормальним. (Хоча результати, які можна шукати проти не шукати, безумовно, є достовірним пунктом.)
— CVn

69

Ви могли бачити, як 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

Я не бачу, як це відповідає на питання. Чи можете ви пояснити, що тут відбувається?
— Ієн Самуель Маклін Старійшина

10
straceпоказує, що системні дзвінки tailроблять під час запуску. Деякі відомості про системні дзвінки ви можете прочитати тут en.wikipedia.org/wiki/System_call . Коротко - відкрити - відкриває файл і повертає ручку (3 у цьому прикладі), lseekпозиції, де ви збираєтесь читати, і readпросто читає, і як ви бачите, повертає, скільки байтів прочитано,
— Сергій Куренков

2
Отже, аналізуючи системні дзвінки, ви іноді можете зрозуміти, як працює програма.
— Сергій Куренков

26

Оскільки файл може бути розкиданий на диску, я думаю, що він повинен [читати файл послідовно], але я не розумію таких внутрішніх справ.

Як ви тепер знаєте, він tailпросто прагне до кінця файлу (із системним викликом lseek) і працює назад. Але у цитуваному вище зауваженні вам цікаво, "як хвіст знає, де на диску знайти кінець файлу?"

Відповідь проста: Хвіст не знає. Процеси на рівні користувача бачать файли як безперервні потоки, тому всі tailможуть знати, що це зсув від початку файлу. Але у файловій системі "inode" (запис каталогу) файлу пов'язаний зі списком чисел, що позначають фізичне розташування блоків даних файлу. Коли ви читаєте з файлу, ядро ​​/ драйвер пристрою визначає, яка саме частина вам потрібна, визначає його розташування на диску і вибирає його для вас.

Це те, з чим ми маємо операційні системи: тому вам не потрібно турбуватися про те, куди розкидаються блоки вашого файлу.


2

Якщо headчи tail здається, що він читає весь файл, ймовірною причиною є те, що файл містить мало символів нового рядка або їх немає . Я зіткнувся з цим кілька місяців тому дуже великим (гігабайтним) блоком JSON, який був серіалізований без пробілу, навіть не в рядках.

Якщо у вас є голова / хвіст GNU, ви можете використовувати -c Nдля друку першого / останнього N байт замість рядків , але, на жаль, це не є функцією POSIX.


1

Як ви бачите у рядку 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.  */
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.