Хвост читает весь файл?

113

Если я хочу tailтекстовый tailфайл размером 25 ГБ, команда читает весь файл?

Поскольку файл может быть разбросан по диску, я полагаю, что это необходимо, но я плохо разбираюсь в таких внутренностях.

Несчастный кот
источник

Ответы:

119

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

Однако обратите внимание, что у tailнего нет выбора, кроме как прочитать все данные, если они предоставлены без возможности поиска, например, при чтении из канала.

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

jlliagre
источник
14
Блин, слишком быстро :-). Вот соответствующий исходный код . Вывести последние строки N_LINES из конца файла FD. Идите назад по файлу, читая байты 'BUFSIZ' за один раз (за исключением, вероятно, первого), пока мы не достигнем начала файла или не прочитаем NUMBER новые строки.
Патрик
1
Также tail +nбудет прочитан весь файл - сначала найти нужное количество новых строк, затем вывести остальные.
SF.
@SF. Действительно, ответ обновлен.
Jlliagre
4
Обратите внимание, что не все tailреализации делают это или делают это правильно. Например, busybox 1.21.1 не tailработает в этом отношении. Также обратите внимание, что поведение меняется при использовании tailstdin и где 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 узнает, где на диске найти конец файла?"

Ответ прост: Хвост не знает. Процессы пользовательского уровня рассматривают файлы как непрерывные потоки, поэтому все, что tailможно знать, это смещение от начала файла. Но в файловой системе «inode» файла (запись каталога) связана со списком чисел, обозначающих физическое расположение блоков данных файла. Когда вы читаете из файла, ядро ​​/ драйвер устройства выясняет, какая часть вам нужна, определяет ее местоположение на диске и выбирает ее для вас.

Для таких вещей у нас есть операционные системы: вам не нужно беспокоиться о том, где разбросаны блоки вашего файла.

Alexis
источник
2

Если headили tail кажется, что он читает весь файл, вероятная причина состоит в том, что файл содержит мало или вообще не содержит символов новой строки . Я споткнулся об этом несколько месяцев назад с очень большим (гигабайтным) BLOB-объектом JSON, который был сериализован без пробелов, даже в виде строк.

Если у вас GNU head / tail, вы можете использовать -c Nдля печати первых / последних N байтов вместо строк , но, к сожалению, это не функция POSIX.

zwol
источник
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.  */
Seenivasan
источник