основы awk
awk разбивает строки на поля $1..$NF. NR — счётчик записей, NF — количество полей. Пары шаблон/действие выполняются избирательно. BEGIN инициализирует, END выводит итоги. Ассоциативные массивы считают и суммируют по ключам за один проход.
cut извлекает колонки. sort | uniq -c их подсчитывает. sed переписывает. Но ни одна из этих утилит не может сложить столбец чисел, вычислить соотношение или вывести итоговую строку после обработки всего ввода. awk может. Это полноценный мини-язык, построенный вокруг идеи, что текст — поток записей (по умолчанию строк), каждая из которых разбита на поля ($1, $2, … $NF). Для каждой записи awk вычисляет список пар шаблон/действие: если шаблон совпадает, выполняется действие. Блок BEGIN устанавливает состояние до любого ввода; блок END подводит итоги после. Эта модель покрывает 80% анализа логов на лету, генерации отчётов и трансформации данных в одной команде.
К концу урока ты сможешь выводить выбранные поля, фильтровать записи по шаблону, накапливать суммы и выдавать отформатированный итог.
После этого урока ты сможешь обращаться к полям записи через $1–$NF, использовать встроенные переменные NR и NF, писать пары шаблон { действие }, использовать BEGIN для инициализации и END для вывода итогов, задавать пользовательский разделитель полей с -F и накапливать числовые суммы по записям.
awk разбивает каждую строку на поля $1, $2, … $NF. По умолчанию разбиение идёт по пробельным символам (любой набор пробелов или табуляций). $0 — вся строка; $NF — последнее поле независимо от их количества; $(NF-1) — предпоследнее.
echo "alice admin 1024" | awk '{print $1, $3}'
# alice 1024Запятая в print $1, $3 выводит поля, разделённые выходным разделителем полей (по умолчанию пробел). Без запятой — print $1 $3 — поля конкатенируются без разделителя.
-F задаёт пользовательский разделитель входных полей:
awk -F: '{print $1, $7}' /etc/passwdРазбивает по : вместо пробелов. Теперь $1 — имя пользователя, $7 — оболочка — те же колонки, что извлекает cut -d: -f1,7, но с куда большими возможностями в действии.
NR — номер текущей записи (строки); NF — количество полей в текущей записи.
awk '{print NR, NF, $0}' /etc/passwd | head -3
# 1 7 root:x:0:0:root:/root:/bin/bash
# 2 7 daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
# 3 7 bin:x:2:2:bin:/bin:/usr/sbin/nologinNR полезен для: пропуска заголовков (NR > 1), вывода каждой N-й строки (NR % 10 == 0), нумерации строк вывода.
NF полезен для: обнаружения некорректных строк (NF != 7), доступа к последнему полю ($NF), или фильтрации строк с минимальным количеством колонок.
# Пропустить заголовок CSV (строка 1) и вывести строки ровно с 5 полями
awk -F, 'NR > 1 && NF == 5 {print $2, $4}' data.csvПары шаблон/действие: шаблон { действие }. Шаблон — любое выражение; действие выполняется только когда шаблон истинен. Любая из частей необязательна:
- Без шаблона: действие выполняется для каждой записи.
- Без действия: действие по умолчанию —
{print $0}— вывести всю строку.
# Вывести строки, где поле 3 (размер ответа) > 10000
awk '$3 > 10000 {print $1, $3}' access.log
# Вывести строки, содержащие 'ERROR' (шаблон — регулярное выражение)
awk '/ERROR/ {print NR, $0}' app.log
# Вывести строки, НЕ совпадающие с шаблоном
awk '!/DEBUG/' app.logВ одной программе awk можно иметь несколько пар шаблон/действие — awk вычисляет каждую пару для каждой записи, выполняя все совпадающие действия по порядку. Это отличается от if/else: несколько шаблонов могут совпасть с одной записью.
Блоки BEGIN и END выполняются один раз — до и после всего ввода.
BEGIN { ... }— инициализировать переменные, вывести заголовок, задатьFS(разделитель полей).END { ... }— вывести итоги, средние значения, сводки.
awk 'BEGIN { total = 0 }
{ total += $3 }
END { print "Total bytes:", total }' access.logtotal инициализируется в 0 в BEGIN. Для каждой строки total += $3 накапливает третье поле. После всех строк END выводит итог. Это шаблон аккумулятора awk — без промежуточной сортировки и внешних инструментов.
Задание FS в BEGIN эквивалентно -F:
awk 'BEGIN { FS = ":" } { print $1, $7 }' /etc/passwdОбъединение шаблонов, аккумуляторов и END для настоящих сводок.
# Подсчитать строки по HTTP-коду в логе nginx
awk '{status[$9]++}
END { for (code in status) print status[code], code }' access.log \
| sort -rnstatus[$9]++ использует $9 (поле кода состояния) как ключ ассоциативного массива и увеличивает его счётчик. После всего ввода END итерирует массив с for (ключ in массив) и выводит каждый код со счётчиком. Пайп в sort -rn даёт ранжированную таблицу.
Средний размер ответа по коду состояния:
awk '{count[$9]++; total[$9]+=$10}
END { for (c in count) printf "%s avg=%.0f\n", c, total[c]/count[c] }' access.logДва параллельных массива, один проход по файлу. Это шаблон, для которого обычно тянешься к pandas или SQL; awk справляется за одну строку.
Вычислить суммарную и среднюю длительность запросов из лога, где поле 5 — длительность в миллисекундах.
awk 'NR > 1 && $5 ~ /^[0-9]+$/ {
sum += $5
count++
}
END {
if (count > 0)
printf "Запросов: %d Итого: %dms Среднее: %.1fms\n", count, sum, sum/count
else
print "Нет валидных записей"
}' requests.logNR > 1 пропускает строку заголовка. $5 ~ /^[0-9]+$/ проверяет, что поле 5 — чистое целое число перед прибавлением (защита от некорректных строк, где поле равно - или пусто). printf форматирует вывод точно. Блок END защищает от деления на ноль.
Этот шаблон — фильтр-защита в теле, итоги в END — является продакшн-качественной awk-идиомой для числовой агрегации.
▸Почему это работает
На macOS системный awk — это оригинальный one-true-awk (BSD awk), а не GNU awk (gawk). Для шаблонов в этом уроке — $1–$NF, NR, NF, BEGIN, END, ассоциативные массивы, printf — оба ведут себя идентично. Отличия появляются только с продвинутыми возможностями gawk (OFMT, FIELDWIDTHS, gensub()). Для скриптов, требующих расширений gawk, устанавливай его явно (brew install gawk) и вызывай как gawk. Все примеры здесь совместимы с обоими.
▸Частая ошибка
Самые распространённые ошибки awk: (1) программа awk без одинарных кавычек в оболочке — всегда оборачивай программу awk в одинарные кавычки, чтобы оболочка не интерпретировала $1, { и } до того, как awk их увидит; (2) использование == для сравнения чисел, хранящихся как строки — awk автоматически приводит строки к числам в числовом контексте, поэтому $3 > 10 работает корректно даже когда $3 — строка; но $3 == "10" и $3 == 10 могут различаться при наличии пробелов; (3) забыть END при накоплении — переменные в теле накапливаются корректно, но никогда не выводятся без блока END.
Ты запускаешь: awk 'END {print NR}' file.txt. Что это выводит?
awk обрабатывает текст как записи, разбитые на поля $1–$NF. $0 — вся запись; NR — текущий номер строки; NF — количество полей. -F РАЗД задаёт разделитель входных полей. Пары шаблон/действие выполняются избирательно — /regex/, числовые сравнения или булевы выражения как шаблоны. BEGIN инициализирует переменные и заголовки до ввода; END выводит итоги после всего ввода. Ассоциативные массивы (arr[ключ]++) позволяют подсчитывать и суммировать по ключам за один проход. awk — правильный инструмент, когда нужна арифметика, условная логика или структурированный вывод, которые не могут выразить cut, sort и sed по отдельности.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.