Learning Platform
Глоссарий Troubleshooting
Урок 08.05 · 18 мин
Начальный
headtailteeLog rotationStreaming

head: первые N строк или байт

$ head file              # первые 10 строк (default)
$ head -n 20 file        # первые 20 строк
$ head -n 5 file         # первые 5
$ head -c 1024 file      # первые 1024 байта
$ head -n -3 file        # все, кроме последних 3 (отрицательное число!)

head оптимален: открывает файл, читает N строк, закрывает и завершается. На файле в 1TB он прочитает только первые ~10KB.

$ time head -n 10 huge.log
real    0m0.003s   # моментально

head -c — для бинарных файлов

$ head -c 4 image.png | xxd
00000000: 8950 4e47                                .PNG

# Первые 4 байта PNG = magic bytes \x89PNG

Полезно для file type detection (вместо file команды) и для quick-sample data files.

tail: последние N строк или байт

$ tail file              # последние 10 строк
$ tail -n 50 log.log     # последние 50
$ tail -n +5 file        # с 5-й строки до конца (НЕ путать с -n 5)

tail -n +N: skip first N-1 lines

# Пропустить header CSV
$ tail -n +2 data.csv | head
# data starts here, без заголовка

# Пропустить первые 100 строк (например, лицензионное соглашение в начале файла)
$ tail -n +101 README.txt

Знак + критичен: -n 5 это «последние 5 строк», -n +5 это «с 5-й строки до конца».

tail -f: follow

Самая полезная фишка tail для DE:

$ tail -f /var/log/airflow/scheduler.log
2026-05-13 12:34:56 INFO loading DAG sync_orders
2026-05-13 12:34:57 INFO loading DAG load_dim_users
# курсор остаётся, новые строки появляются в реальном времени...

-f (--follow) держит файл открытым и выводит новые строки по мере их появления. Это watch-режим для логов. Прервать — Ctrl+C.

Под капотом: tail делает lseek к концу файла, потом в цикле read() и inotify_wait() (Linux) или polling (BSD).

tail -F: follow with re-open

Различия -f vs -F:

$ tail -f /var/log/app.log
# Когда log rotation удалит app.log и создаст новый —
# tail продолжит читать СТАРЫЙ inode, который больше не растёт.
# Новых строк не увидишь.

$ tail -F /var/log/app.log
# tail -F отслеживает имя файла. Если файл исчез/пересоздался —
# tail откроет новый файл и продолжит.

-F — must-have для production логов под logrotate. Если log утром был ротирован, -f подвиснет на старом inode, -F подцепит новый файл автоматически.

# Эквивалент:
$ tail --follow=name --retry /var/log/app.log

tail -f и пайпы

# Real-time filter:
$ tail -f /var/log/api.log | grep ERROR

# С контекстом:
$ tail -f /var/log/api.log | grep -C 2 ERROR

ВНИМАНИЕ: Внимание: grep в pipeline буферизует output. Если данные приходят медленно, ты увидишь их с задержкой. Решение — --line-buffered:

$ tail -F /var/log/app.log | grep --line-buffered ERROR

Или stdbuf -oL grep ... (force line-buffered). Иначе grep будет копить ~4KB прежде чем выводить.

DE-кейсы tail -F

1. Watch Airflow scheduler

$ tail -F /var/log/airflow/scheduler.log | grep -E '(ERROR|Task)'

2. Multi-file watch

$ tail -F /var/log/airflow/*.log
==> /var/log/airflow/scheduler.log <==
INFO loading DAG ...

==> /var/log/airflow/worker.log <==
ERROR connection timeout

==> /var/log/airflow/dag_processor.log <==
INFO parsing DAGs...

tail -F на множестве файлов добавляет header ==> filename <== перед каждой группой строк. Удобно для одновременного мониторинга разных компонентов.

3. Tail с компрессированными логами

$ zcat /var/log/airflow/scheduler.log.5.gz | tail -100

zcat декомпрессирует на лету, tail берёт последние 100 строк. Альтернатива: tail -100 <(zcat file.gz).

tee: развилка stdout

tee (от “T-fitting” в водопроводе) — копирует stdin в stdout И файл одновременно:

$ command | tee output.log
# command's stdout -> output.log AND видимо на экране
$ ls -l | tee files.txt
total 24
-rw-r--r-- 1 levo levo  321 May 13 README.md
-rw-r--r-- 1 levo levo 1024 May 13 data.csv

$ cat files.txt
total 24
-rw-r--r-- 1 levo levo  321 May 13 README.md
-rw-r--r-- 1 levo levo 1024 May 13 data.csv

Идеальная команда когда хочешь видеть вывод и сохранить его одновременно.

tee -a: append

$ command | tee -a log.txt
# Не перезаписывает log.txt, дописывает в конец.

tee для sudo-write

# [X] НЕ РАБОТАЕТ: redirect выполняется shell-ом ПЕРЕД sudo
$ sudo echo "127.0.0.1 myhost" > /etc/hosts
# Permission denied — shell пытается открыть /etc/hosts для записи под своим UID

# [x] tee решает: sudo применяется к tee, который пишет в /etc/hosts
$ echo "127.0.0.1 myhost" | sudo tee -a /etc/hosts
127.0.0.1 myhost      # вывод tee (его stdout)
$ cat /etc/hosts | tail -1
127.0.0.1 myhost      # запись прошла

Это классический idiom для записи в root-only файлы из обычной shell.

tee + process substitution

# Слать в файл errors.log и одновременно делать grep WARN
$ command | tee >(grep ERROR > errors.log) | grep WARN

>(...) — process substitution (про это в модуле 9). tee получает stdin, копирует в файл-аналог >(grep ERROR > errors.log) (внутри которого grep пишет в errors.log) и в stdout (откуда дальше идёт | grep WARN).

Это позволяет разветвить pipeline в несколько направлений одновременно.

# Логи в три файла одновременно
$ command | tee >(grep ERROR > errors.log) >(grep WARN > warnings.log) > all.log

Pipeline pattern: command | tee output.log | grep CRITICAL

$ ./run-etl.sh | tee /var/log/etl/full.log | grep -E '(ERROR|CRITICAL)'

Полный лог пишется в full.log, на экран попадает только подсветка ошибок. После выполнения — full.log есть для post-mortem.

Combine: head + tail для slicing

# Строки с 100-й по 200-ю
$ head -n 200 file | tail -n 101    # верхние 200, потом последние 101 из них

# или через sed:
$ sed -n '100,200p' file

Попробуй сам

  1. Базовое:
    head -n 5 /etc/passwd
    tail -n 5 /etc/passwd
    head -c 100 /etc/passwd
  2. Tail follow:
    # В одном терминале:
    tail -F /tmp/test.log
    
    # В другом:
    echo "$(date) hello" >> /tmp/test.log
    echo "$(date) world" >> /tmp/test.log
    # Должен увидеть строки в первом терминале
  3. Skip header:
    printf "header\na\nb\nc\n" | tail -n +2
    # вывод: a, b, c
  4. tee для split:
    ls /etc | tee /tmp/etc.txt | wc -l
    # tee копирует в /tmp/etc.txt, wc -l получает stdin
    # Видишь число файлов
    cat /tmp/etc.txt | head

macOS-различия

  • head, tail, tee на macOS работают идентично GNU-версиям для базовых флагов.
  • tail -F: на macOS работает через kqueue (BSD), на Linux — через inotify. Поведение для пользователя одинаковое.
  • tail -n -3 (отрицательное = «все, кроме последних 3») — поддерживается обеими.
  • head -c на BSD требует число без единиц (head -c 1024), GNU поддерживает суффиксы (head -c 1K).
Проверка знанийKnowledge check
Logrotate ежедневно ротирует /var/log/api.log: переименовывает в api.log.1, gzip'ит вчерашний в api.log.1.gz, создаёт новый пустой api.log. Ты запустил 'tail -f /var/log/api.log' вечером 13-го. В полночь logrotate сработал. На следующее утро ты ожидаешь увидеть новые строки от 14-го числа. Увидишь ли?
ОтветAnswer
Нет. tail -f использует file descriptor (FD), который привязан к inode при первом open(). После logrotate inode со старыми данными сохраняется (но имя 'api.log' теперь указывает на НОВЫЙ inode). Старый inode не удаляется, пока его FD держит tail. Новые записи пишутся в НОВЫЙ inode, который tail не видит. tail продолжает читать старый файл (api.log.1 после переименования) и видит только новые строки, которые туда добавились — а их нет, потому что приложение пишет в новый api.log. Решение: использовать tail -F (или --follow=name --retry). -F отслеживает не FD, а ИМЯ файла. Когда logrotate переименовывает файл, tail -F видит, что 'api.log' теперь другой файл, закрывает старый FD, открывает новый и продолжает следовать. Это must-have для production-логов под logrotate. Альтернатива: configure logrotate с copytruncate (копирует и обрезает существующий файл вместо переименования) — тогда даже -f сохранит FD. Но copytruncate имеет race-condition: записи между copy и truncate теряются.

Главное

  • head -n N — первые N строк, head -c N — первые N байт.
  • head -n -N — все, кроме последних N (отрицательное).
  • tail -n N — последние N строк, tail -n +Nс N-й строки.
  • tail -f — следить за файлом (FD-based). tail -F — следить по имени (handles log rotation).
  • tee FILE — копирует stdin в файл И stdout. -a append. Идиома ... | sudo tee /etc/file для записи в root-файлы.
  • tee >(cmd1) >(cmd2) — разветвление pipeline в несколько направлений (process substitution).
  • Для real-time filter добавь --line-buffered в grep, чтобы не было buffer-delay.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 4. В чём принципиальная разница между 'tail -f log' и 'tail -F log'?

Закончили урок?

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

Войдите чтобы оценить урок

Прогресс модуля
0 из 5