awk и sed: обрабатываем логи без открытия файлов
Друзья, давайте честно: сколько раз вы пытались открыть лог-файл размером в пару гигабайт в текстовом редакторе? И сколько раз редактор "не отвечал", "зависал", "кушал всю память" и просто "умирал"?
Я через это проходил. Notepad++ падал, Sublime Text думал минут по пять, а Vim просто говорил "ну ты чего, это же 500 мегабайт, я не камикадзе".
И только потом до меня дошло: лог-файлы не предназначены для открытия. Их предназначение — быть обработанными. А для этого есть два древних, но бессмертных инструмента: awk и sed.
Сегодня я расскажу, как с их помощью вытворять с логами такое, что в обычном редакторе заняло бы часы ручной работы.
Почему awk и sed, а не Python
Я люблю Python. Но когда нужно быстро посмотреть, что происходит в логе "на живую", писать скрипт — это как вызывать сантехника, чтобы лампочку поменять.
awk и sed — это швейцарские ножи консоли. Они:
- Не загружают весь файл в память (читают построчно)
- Работают мгновенно даже на гигабайтных файлах
- Есть на любом Linux-сервере по умолчанию
- Позволяют делать сложные преобразования одной строкой
Выучив базовые приёмы, вы сэкономите себе часы жизни.
Sed: король замен и удалений
sed (Stream EDitor) — это редактор потоков. Он читает файл построчно, применяет к строке команды и выводит результат.
Базовый синтаксис
bash
sed 'команда' файл
Самые частые команды:
- s/что/на что/ — замена (substitute)
- /шаблон/d — удалить строки, подходящие под шаблон
- p — печать (обычно с флагом -n)
Замена текста (kill -9 ошибкам)
Допустим, в логе много путей вида /home/user/project/logs/..., а нам нужно коротко.
bash
sed 's/\/home\/user\/project\/logs\///g' app.log
Проблема: слеши экранировать муторно. Используем другой разделитель:
bash
sed 's|/home/user/project/logs/||g' app.log
Уже лучше. А теперь реальный пример: в логах nginx IP-адреса вида 192.168.1.100, а нам нужно заменить последний октет на XXX для анонимизации:
bash
sed -E 's/([0-9]+\.[0-9]+\.[0-9]+\.)[0-9]+/\1XXX/g' access.log
-E включает расширенные регулярки, чтобы не экранировать скобки и плюсы.
Удаление мусора
В логах часто бывают строки, которые только мешают. Например, бесконечные "heartbeat" сообщения.
bash
sed '/heartbeat/d' app.log
Или удалить всё, кроме ошибок:
bash
sed -n '/ERROR/p' app.log
Флаг -n говорит "не печатай ничего по умолчанию", а p в конце — "напечатай, если нашёл ERROR".
Удаление ANSI-цветов
Если вы когда-нибудь перенаправляли вывод цветного приложения в файл, то знаете эту боль: ^[[31mОшибка^[[0m. Удаляем:
bash
sed -E 's/\x1b\[[0-9;]*m//g' colorful.log
Работа с диапазонами
Иногда нужно вырезать кусок лога между двумя маркерами. Например, с момента запуска до первой ошибки:
bash
sed -n '/START/,/ERROR/p' app.log
Это напечатает все строки от первой встретившейся "START" до первой "ERROR" включительно.
Редактирование "на месте"
Флаг -i (in-place) меняет файл без создания нового. Осторожно!
bash
sed -i.bak 's/старое/новое/g' app.log
Создаст резервную копию app.log.bak и изменит оригинал.
Awk: король полей и отчётов
Если sed — это про замену текста, то awk — это про работу со структурированными данными. Он воспринимает строку как набор полей и позволяет с ними работать.
Базовый синтаксис
bash
awk '/шаблон/ {действие}' файл
По умолчанию поля разделяются пробелами или табами. К полям обращаются как $1, $2, $0 — вся строка.
Вырезаем колонки
Классика: лог nginx — вытащить только IP и URL.
bash
awk '{print $1, $7}' access.log
$1 — IP, $7 — обычно URL (зависит от формата лога).
Фильтрация по значениям
Показать только запросы, которые вернули 500-ю ошибку:
bash
awk '$9 == 500 {print $1, $7, $9}' access.log
$9 — код ответа.
Статистика одной строкой
Сколько запросов от каждого IP?
bash
awk '{count[$1]++} END {for (ip in count) print ip, count[ip]}' access.log | sort -rn -k2
Разбираем:
- {count[$1]++} — для каждого уникального $1 увеличиваем счётчик
- END {for ...} — после обработки всего файла печатаем результат
- sort -rn -k2 — сортируем по убыванию второго поля (числа запросов)
Работа с CSV и TSV
Если разделитель не пробел, задаём свой:
bash
awk -F ',' '{print $1, $3}' data.csv
-F ',' — разделитель запятая.
Арифметика
Посчитать среднее время ответа (если в логе есть поле с временем):
bash
awk '{sum += $10; count++} END {print "Average:", sum/count}' access.log
Условные операторы
Показать запросы, которые длились дольше 5 секунд:
bash
awk '$10 > 5000 {print $1, $7, $10}' access.log
Регулярки
Найти все запросы к админке:
bash
awk '$7 ~ /admin/ {print $1, $7}' access.log
Форматированный вывод
printf работает как в Си:
bash
awk '{printf "IP: %-15s URL: %s\n", $1, $7}' access.log
Связка команд: конвейер смерти
Настоящая магия начинается, когда вы комбинируете sed, awk, grep, sort, uniq и head в конвейерах.
Пример 1: Топ-10 IP по количеству запросов
bash
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
awkвырезает IPsortсортируетuniq -cсчитает уникальныеsort -rnсортирует по убыванию чиселhead -10берёт первые 10
Пример 2: Запросы с 404-й ошибкой, сгруппированные по URL
bash
awk '$9 == 404 {print $7}' access.log | sort | uniq -c | sort -rn
Пример 3: Активность по часам
bash
awk '{print substr($4, 14, 2)}' access.log | sort | uniq -c
Если время в логе в формате [03/Mar/2026:14:23:45, то substr($4, 14, 2) вытащит час (14).
Пример 4: Очистка и подсчёт ошибок в Java-логах
Java-логи часто многострочные. Сначала склеим стектрейсы, потом посчитаем:
bash
sed '/^[0-9]/!{:a;N;s/\n/ /;ta}' app.log | grep ERROR | wc -l
Это сложное заклинание, но оно работает: склеивает строки, которые не начинаются с цифры (продолжение стека), с предыдущей строкой.
Реальные кейсы из жизни
Кейс 1: Почистить пароли из лога
Случайно залогировали POST-запрос с паролем. Нужно срочно вычистить все упоминания.
bash
sed -i 's/password=[^&]*/password=***/g' access.log
Кейс 2: Посчитать количество уникальных ошибок
В логе приложения ошибки вида ERROR: что-то пошло не так. Хотим понять, какие ошибки самые частые.
bash
grep ERROR app.log | sed 's/.*ERROR: //' | sort | uniq -c | sort -rn
Кейс 3: Преобразовать timestamp в другой формат
Было: 2026-03-03 14:23:45 ERROR ...
Надо: 03/03/2026 14:23:45 ERROR ...
bash
sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/' app.log
Кейс 4: Вытащить все IP из лога (даже если они внутри строки)
bash
grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' app.log | sort -u
grep -o выводит только совпадения, а не всю строку.
Полезные однострочники (шпаргалка)
Sed
```bash
Удалить пустые строки
sed '/^$/d' file
Удалить комментарии
sed '/^#/d' file
Заменить табуляцию на пробелы
sed 's/\t/ /g' file
Добавить строку в начало файла
sed '1i\Новая первая строка' file
Удалить последние 5 строк
sed -n ':a;N;$!ba;s/\n/ /g' file # сложно, но работает ```
Awk
```bash
Вывести строки с 10 по 20
awk 'NR>=10 && NR<=20' file
Вывести последнее поле каждой строки
awk '{print $NF}' file
Пропустить первые 10 строк
awk 'NR>10' file
Посчитать сумму чисел во втором поле
awk '{sum+=$2} END {print sum}' file
Вывести строки, где длина больше 80 символов
awk 'length($0) > 80' file ```
Производительность: почему это быстро
Когда вы открываете гигабайтный файл в редакторе, он пытается загрузить его в память целиком. Это убийственно.
Когда вы делаете sed 's/что-то/на что-то/' bigfile.log, происходит следующее:
- Файл читается блоками (буферизованно)
- Каждый блок обрабатывается
- Результат пишется (или выводится)
- Память не растёт
Даже на файлах в десятки гигабайт sed и awk будут работать, пока есть место на диске для результата.
Типичные ошибки новичков
Ошибка 1: Забыть про кавычки
```bash
Неправильно
awk {print $1} file
Правильно
awk '{print $1}' file ```
Ошибка 2: Путать $ в awk и $ в shell
Внутри одинарных кавычек переменные shell не подставляются:
```bash
Неправильно (если переменная IP=192.168)
awk '/$IP/' file
Правильно
awk '/'$IP'/' file # закрыли кавычки, вставили переменную, открыли снова ```
Ошибка 3: Игнорировать экранирование
В регулярках много спецсимволов. Точка — любой символ, звёздочка — повторение.
```bash
Неправильно (найдёт "a" и что угодно после)
sed 's/a*/b/' file
Правильно (ищем буквально звёздочку)
sed 's/*/звёздочка/g' file ```
Ошибка 4: Менять файл без проверки
Всегда сначала проверяйте без -i:
bash
sed 's/важное/очень важное/' file | head -20
Посмотрели, что всё ок — потом уже -i.
Интерактивный режим: работа с большими файлами
Если нужно "полазить" по огромному файлу, не открывая его целиком:
bash
less bigfile.log
В less можно:
- /ошибка — найти слово "ошибка"
- n — следующее совпадение
- N — предыдущее
- G — в конец файла
- g — в начало
- F — следить за обновлениями (как tail -f)
А если нужно посмотреть конкретные строки, sed и awk справятся:
```bash
Показать строки с 1000 по 2000
sed -n '1000,2000p' bigfile.log
Показать строки с 5000 до конца
sed -n '5000,$p' bigfile.log ```
Что дальше?
awk и sed — это только начало. Когда вы освоите их, следующим шагом будут:
- jq — для работы с JSON-логами
- xpath — для XML-логов
- perl — если нужно совсем сложное преобразование
Но для 95% повседневных задач хватает связки grep | sed | awk | sort | uniq.
Коротко: шпаргалка
| Задача | Команда |
|--------|---------|
| Заменить текст | sed 's/старое/новое/g' |
| Удалить строки | sed '/шаблон/d' |
| Вырезать колонки | awk '{print $1, $2}' |
| Посчитать уникальные | sort \| uniq -c \| sort -rn |
| Фильтр по условию | awk '$9 == 500' |
| Статистика | awk '{count[$1]++} END {for (i in count) print i, count[i]}' |
| Сумма чисел | awk '{sum+=$2} END {print sum}' |
Вместо заключения
Я понимаю, что поначалу awk и sed выглядят как китайская грамота. Слишком много долларов, слешей и фигурных скобок. Но поверьте: это как учиться ездить на велосипеде. Первые полчаса страшно, потом — уже не представляешь жизни без него.
Начните с малого. Сегодня вырежьте IP из лога. Завтра посчитайте топ-10 ошибок. Послезавтра автоматизируйте еженедельный отчёт одной командой.
И никогда больше не открывайте гигабайтные логи в редакторе. Это прошлый век. Настоящие сисадмины смотрят на логи, не открывая файлов. Будьте как настоящие сисадмины.