Если я хочу tail
текстовый tail
файл размером 25 ГБ, команда читает весь файл?
Поскольку файл может быть разбросан по диску, я полагаю, что это необходимо, но я плохо разбираюсь в таких внутренностях.
Если я хочу tail
текстовый tail
файл размером 25 ГБ, команда читает весь файл?
Поскольку файл может быть разбросан по диску, я полагаю, что это необходимо, но я плохо разбираюсь в таких внутренностях.
Ответы:
Нет, tail
не читает весь файл, он стремится к концу, затем читает блоки назад, пока не будет достигнуто ожидаемое количество строк, затем отображает строки в правильном направлении до конца файла и, возможно, продолжает отслеживать файл, если -f
опция используется.
Однако обратите внимание, что у tail
него нет выбора, кроме как прочитать все данные, если они предоставлены без возможности поиска, например, при чтении из канала.
Точно так же, когда его просят искать строки, начинающиеся с начала файла, с использованием tail -n +linenumber
синтаксиса или tail +linenumber
нестандартной опции, когда поддерживается, tail
очевидно, читает весь файл (если он не прерван).
tail +n
будет прочитан весь файл - сначала найти нужное количество новых строк, затем вывести остальные.
tail
реализации делают это или делают это правильно. Например, busybox 1.21.1 не tail
работает в этом отношении. Также обратите внимание, что поведение меняется при использовании tail
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 узнает, где на диске найти конец файла?"
Ответ прост: Хвост не знает. Процессы пользовательского уровня рассматривают файлы как непрерывные потоки, поэтому все, что tail
можно знать, это смещение от начала файла. Но в файловой системе «inode» файла (запись каталога) связана со списком чисел, обозначающих физическое расположение блоков данных файла. Когда вы читаете из файла, ядро / драйвер устройства выясняет, какая часть вам нужна, определяет ее местоположение на диске и выбирает ее для вас.
Для таких вещей у нас есть операционные системы: вам не нужно беспокоиться о том, где разбросаны блоки вашего файла.
Если head
или tail
кажется, что он читает весь файл, вероятная причина состоит в том, что файл содержит мало или вообще не содержит символов новой строки . Я споткнулся об этом несколько месяцев назад с очень большим (гигабайтным) BLOB-объектом JSON, который был сериализован без пробелов, даже в виде строк.
Если у вас GNU head / tail, вы можете использовать -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. */