Конвейеры

Обновлено 30 августа 2026 · 6 мин чтения

Почти любой вопрос к логу сводится к одной из трёх операций: посчитать частоты, просуммировать поле, отобрать строки по условию. Дальше — комбинации.

Частоты

Базовый счётчик

Идиома, которую стоит помнить наизусть: выбрать поле, отсортировать, схлопнуть с подсчётом, отсортировать по числу.

awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -20

Порядок важен. uniq схлопывает только соседние одинаковые строки, поэтому sort перед ним обязателен.

Распределение кодов ответа

awk '{print $9}' access.log | sort | uniq -c | sort -rn

Запросы по минутам

Вырезаем из поля времени нужную длину. У nginx поле $4 выглядит как [30/Aug/2026:09:14:02, поэтому начинаем со второго символа.

awk '{print substr($4, 2, 17)}' access.log | uniq -c

Суммы и средние

Сколько отдали трафика

Десятое поле — размер ответа в байтах.

awk '{s += $10} END {printf "%.1f MB\n", s/1024/1024}' access.log

Среднее время ответа по URL

Ассоциативные массивы в awk — то, ради чего его стоит знать дальше print $1.

awk '{sum[$7] += $NF; cnt[$7]++}
END {for (u in sum) printf "%8.3f %6d %s\n", sum[u]/cnt[u], cnt[u], u}' access.log \
| sort -rn | head -20

$NF — последнее поле строки, каким бы по счёту оно ни было. Удобно, когда формат лога плавает.

Поля и куски

cut, когда разделитель постоянный

Быстрее awk и короче пишется, но не умеет схлопывать повторяющиеся разделители.

cut -d' ' -f1,7,9 access.log | head

Диапазон строк

sed -n с диапазоном печатает строки с 1000-й по 1050-ю и останавливается, если добавить q.

sed -n '1000,1050p' app.log
sed -n '1000,1050p;1051q' app.log

Выровнять в таблицу

Финальный штрих, когда вывод нужно показать человеку.

awk '{print $1, $9, $10}' access.log | head -20 | column -t

Параллель

Обработать много файлов сразу

-P задаёт число параллельных процессов, -I{} — плейсхолдер для имени.

ls /var/log/nginx/*.gz | xargs -P 4 -I{} sh -c 'zgrep -c ERROR {} | xargs echo {}'

Имена с пробелами

Обычный xargs ломается на пробелах и переносах в именах. Связка -print0 и -0 передаёт имена через нулевой байт.

find /var/log -name '*.log' -print0 | xargs -0 -P 4 grep -l 'panic'

Что сохраняет вечера

Строгий режим

#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'

pipefail — главное здесь. По умолчанию код возврата конвейера равен коду последней команды. cat missing.log | wc -l печатает ноль и рапортует об успехе, а скрипт спокойно идёт дальше с пустыми данными.

Временный файл, который сам уберётся

trap на EXIT срабатывает и при нормальном завершении, и при ошибке под set -e.

tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT
grep ERROR app.log > "$tmp"
wc -l < "$tmp"

Подстановка процесса

Сравнить вывод двух команд без временных файлов.

diff <(awk '{print $1}' old.log | sort -u) \
<(awk '{print $1}' new.log | sort -u)

Значение переменной по динамически собранному имени

Косвенное раскрытие через ${!name}. Пригождается, когда список параметров приходит извне.

A=1;
B=2;
C=3;
VARS="A B C"
for key in $VARS; do
val=${!key};
echo "$key=$val";
done;
var-log.online — личные заметки по администрированию. Примеры проверены на Debian и Ubuntu.