awk: язык обработки таблиц и отчётов
awk видит каждую строку как набор полей, разбитых по разделителю, и позволяет писать на них целые программы — от выборки колонки до сводного отчёта.
awk — это не команда, а маленький язык программирования для построчной обработки текста, разбитого на поля. Программа awk — это набор правил вида
паттерн { действие }: для каждой строки, подошедшей под паттерн, выполняется действие.
Там, где sed силён в подстановках, awk силён в работе со столбцами: посчитать сумму по колонке, отфильтровать строки по числовому условию, переформатировать таблицу, сгруппировать и агрегировать. Это незаменимый инструмент для отчётов из логов, разбора вывода ps/df/netstat и быстрой аналитики прямо в терминале — без выгрузки в таблицы.
Поля: $1, $2, ... $NF
awk автоматически разбивает строку на поля по пробелам/табам (последовательность пробелов считается одним разделителем) и кладёт их в переменные $1, $2 и так далее. $0 — вся строка целиком, $NF — последнее поле.
# первое и третье поле каждой строки
awk '{ print $1, $3 }' data.txt
# последнее поле (NF — число полей; $NF — само поле)
awk '{ print $NF }' data.txt
# поменять разделитель ввода на запятую (CSV): -F','
awk -F',' '{ print $2 }' users.csv
Запятая в print вставляет между значениями OFS (output field separator), по умолчанию пробел. Без запятой значения склеиваются вплотную.
Встроенные переменные: NR и NF
| Переменная | Смысл |
NR | Number of Record — номер текущей строки (нарастающий счётчик) |
NF | Number of Fields — сколько полей в текущей строке |
FS / OFS | разделитель полей на входе / на выходе |
FILENAME | имя текущего обрабатываемого файла |
# пронумеровать строки (аналог nl)
awk '{ print NR, $0 }' file.txt
# напечатать только строки, где ровно 3 поля
awk 'NF == 3' file.txt
# напечатать строки с 5-й по 10-ю
awk 'NR >= 5 && NR <= 10' file.txt
Паттерны и действия
Перед фигурными скобками можно поставить условие — действие выполнится только для подошедших строк. Если действие опущено, по умолчанию это { print $0 }. Если опущен паттерн — действие применяется ко всем строкам.
# строки, где первое поле больше 100 (числовое сравнение)
awk '$1 > 100' numbers.txt
# строки, где третья колонка равна слову ERROR
awk '$3 == "ERROR" { print $1, $2 }' app.log
# строки, совпавшие с регуляркой по всей строке
awk '/timeout/ { print NR": "$0 }' app.log
# регулярка по конкретному полю: ~ означает «совпадает»
awk '$2 ~ /^5[0-9]{2}$/ { print }' access.log
BEGIN и END
Два специальных паттерна: блок BEGIN { } выполняется один раз до чтения данных, блок END { } — один раз после последней строки. Это идеальное место для заголовков таблицы и для печати итогов накопленных сумм.
# сумма по первой колонке
awk '{ sum += $1 } END { print "Итого:", sum }' amounts.txt
# заголовок, тело, подвал
awk 'BEGIN { print "=== ОТЧЁТ ===" }
{ print $1, $2 }
END { print "Строк обработано:", NR }' data.txt
Переменные, арифметика, printf
В awk есть полноценные переменные (без объявления, инициализируются нулём или пустой строкой), арифметика, строки и ассоциативные массивы. Для аккуратного форматирования есть printf в стиле C.
# среднее по колонке
awk '{ s += $1; n++ } END { printf "Среднее: %.2f\n", s/n }' nums.txt
# выровненная таблица: %-15s — строка по левому краю шириной 15, %8.2f — число
awk '{ printf "%-15s %8.2f\n", $1, $2 }' prices.txt
# вычислить колонку: цена * количество
awk -F',' '{ printf "%s: %d\n", $1, $2 * $3 }' orders.csv
В printf переносы строк не добавляются автоматически — пишите \n сами, в отличие от print.
Агрегация по столбцам: ассоциативные массивы
Главная суперсила awk — ассоциативные массивы с произвольным строковым ключом. Это готовый «GROUP BY» из SQL прямо в терминале: накапливаем значения по ключу, в END печатаем сводку.
# сколько раз встречается каждое значение во 2-й колонке
awk '{ count[$2]++ } END { for (k in count) print k, count[k] }' data.txt
# сумма продаж по менеджеру (колонка 1 — имя, колонка 3 — сумма)
awk -F',' '{ total[$1] += $3 }
END { for (m in total) printf "%-12s %10.2f\n", m, total[m] }' sales.csv
Поскольку count[$2]++ создаёт ключ при первом обращении, отдельная инициализация не нужна — несуществующий элемент массива в арифметике считается нулём.
Как это работает под капотом
Движок awk — это цикл по записям. Он читает вход порциями-записями (по умолчанию запись = строка, разделитель записей RS = "\n"), каждую запись разбивает на поля по FS и наполняет $1..$NF и счётчики NR/NF. Затем по порядку проверяются все правила паттерн { действие }; для подошедших выполняется тело. После исчерпания входа отрабатывает END. Память между строками сохраняется в обычных переменных и массивах, поэтому накопление сумм и группировка не требуют второго прохода. На практике вы встретите три реализации: классический awk, nawk и самый распространённый на Linux gawk (GNU awk) с расширениями — функциями по работе со временем, сортировкой массивов asort и переключателем FS на регулярные выражения. Важно понимать разницу типов: awk слабо типизирован, и сравнение $1 == "10" (строковое) и $1 == 10 (числовое) могут дать разный результат для значения вроде 10.0.
Частые ошибки
- Кавычки. Программу awk заключают в одинарные кавычки, чтобы
$1не подменила оболочка. Двойные кавычки приведут к тому, что bash попытается раскрыть$1как свой аргумент. - FS только для одиночного символа.
-F','— это запятая. Но если поля разделены несколькими пробелами и есть пустые поля (как в CSV с пустыми значениями), стандартное разбиение «схлопывает» пробелы — для строгого CSV нуженFS=",", а не пробельный режим. - print против printf.
printfне ставит перенос строки сам — забытый\nсклеит весь вывод в одну строку. - Сравнение строк и чисел. Поля приходят как строки;
$1 > 9при значении"100"сравнится численно, а$1 > "9"— лексикографически (и «100» окажется меньше «9»). Будьте явны. - Порядок в END-цикле.
for (k in arr)не гарантирует порядок ключей. Нужен отсортированный вывод — пропускайте результат черезsort.
Итоги
- awk — язык построчной обработки полей:
$1..$NF— поля,$0— вся строка,$NF— последнее поле. NR— номер строки,NF— число полей;-Fзадаёт разделитель ввода.- Правила имеют вид
паттерн { действие }; паттерном может быть условие, регулярка или диапазон. BEGIN/ENDвыполняются один раз до и после данных — для заголовков и итогов.- Ассоциативные массивы (
count[$2]++,total[$1]+=$3) дают группировку и агрегацию — «GROUP BY» в терминале.