head и tail
Первые и последние строки файла, срезы по номерам и слежение за растущим логом через tail -f
head печатает начало файла, tail — конец. По умолчанию — 10 строк. tail -f не завершается, а продолжает выводить новые строки по мере того, как они дописываются в файл, — так смотрят логи в реальном времени.
Синтаксис
head [-n N] [файл…]
tail [-n N] [файл…]
tail -f | -F [-n N] файл…
Без файлов (или с файлом -) обе команды читают stdin.
Опции head
| Опция | Что делает |
|---|---|
-n 5 (-5) | первые 5 строк |
-n -5 | всё, кроме последних 5 строк |
-c 100 | первые 100 байт; -c -100 — всё, кроме последних 100 байт |
-q | не печатать заголовки ==> файл <== при нескольких файлах |
-v | печатать заголовок, даже если файл один |
-z | строки разделены нулевым байтом, а не \n |
Опции tail
| Опция | Что делает |
|---|---|
-n 5 (-5) | последние 5 строк |
-n +5 | всё, начиная с 5-й строки (пропустить первые 4) |
-c 100 / -c +100 | последние 100 байт / всё, начиная со 100-го байта |
-q / -v | не печатать / всегда печатать заголовки файлов |
-z | строки разделены нулевым байтом |
Числа в -n и -c принимают суффиксы: K = 1024, M = 1024², kB = 1000 и т. д. (head -c 1K).
Слежение за файлом
| Опция | Что делает |
|---|---|
-f | после вывода не завершаться, а печатать новые строки по мере дописывания. Следит за открытым файлом (--follow=descriptor) |
--follow=name | следить за именем: если файл переименовали или удалили и создали заново, переоткрыть его |
--retry | продолжать попытки открыть файл, если его пока нет или он недоступен |
-F | то же, что --follow=name --retry — правильный выбор для логов с ротацией |
--pid=PID | с -f: завершиться, когда процесс PID закончится |
-s N | с -f: проверять файл раз в N секунд (по умолчанию 1; при inotify редко нужно) |
--max-unchanged-stats=N | с --follow=name: переоткрывать файл, если размер не менялся N проверок (по умолчанию 5) |
При слежении за несколькими файлами tail печатает заголовок ==> файл <== каждый раз, когда переключается на другой файл. Об усечении и замене файла он сообщает в stderr: file truncated, has been replaced; following new file.
Примеры
seq 100 > nums.txt
# первые и последние 3 строки
head -n 3 nums.txt
tail -n 3 nums.txt
# пропустить строку-заголовок (например, у CSV или вывода ps)
tail -n +2 nums.txt | head -n 3
ps aux | tail -n +2 | wc -l
# строки с 20-й по 25-ю
head -n 25 nums.txt | tail -n +20
sed -n '20,25p' nums.txt
# всё, кроме последних 95 строк
head -n -95 nums.txt
# первые 16 байт файла — посмотреть сигнатуру
head -c 16 /bin/ls | od -c
# начало каждого из нескольких файлов, с заголовками и без
head -n 1 /etc/passwd /etc/group
head -q -n 1 /etc/passwd /etc/group
# следить за логом: показать последние 20 строк и ждать новых (Ctrl+C — выход)
tail -n 20 -f app.log
# следить по имени — переживёт ротацию лога и появление файла позже
tail -F app.log
# следить за несколькими логами сразу
tail -F app.log error.log
# следить и фильтровать на лету
tail -F app.log | grep --line-buffered ERROR
# следить ограниченное время
timeout 10 tail -f app.log
# следить, пока работает фоновый процесс, и завершиться вместе с ним
sleep 5 &
tail -f app.log --pid=$!
Подводные камни
tail -n +5— это «начиная с 5-й», а не «последние 5». Знак плюс меняет смысл; уheadособый смысл у минуса:head -n -5.tail -fдержится за открытый файл. Если лог переименуют при ротации (app.log→app.log.1) и создадут новый,tail -fпродолжит читать старый файл и новых строк не увидит. Для логов, которые ротируются, используйtail -F. Если файл просто обрезают (> app.log), GNU tail замечает это и пишетfile truncated.tail -fникогда не завершается сам. В скрипте он повиснет; ограничь его черезtimeoutили--pid.- Буферизация в конвейере. В
tail -f … | grep … | …grep копит вывод в буфер, и строки появляются с задержкой. Помогаетgrep --line-buffered(у sed —-u, у других программ —stdbuf -oL). - Нумерация с единицы.
tail -n +1выводит файл целиком,tail -n +2пропускает первую строку. - Без
\nв конце последняя «строка» всё равно считается строкой:printf 'a\nb' | tail -n 1выведетb(без перевода строки). headв конвейере закрывает канал рано. Команда слева может получить SIGPIPE и завершиться с ошибкой — сset -o pipefailэто делает весь конвейер неуспешным.