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


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.