Зачем awk
Когда текст разбит по колонкам (логи, df, ps, CSV) - awk бьёт его
на поля и даёт привычные численные операции. [[cmd-sed|sed]] силён
построчно, awk - силён "по полям + счётчики + агрегация". Между ними и
Python'ом - граница: до 30 строк awk-скрипта или таблица в 5 колонок -
awk выгоднее.
В Linux обычно gawk (GNU awk) или mawk (быстрее, минималистичнее).
POSIX-сабсет работает везде, GNU-расширения (gensub, multi-dim arrays
через ;, asort) - только в gawk.
Базовый синтаксис
awk 'pattern { action }' fileawk -F: '{ print $1 }' /etc/passwdawk -f script.awk file
- pattern - regex
/foo/, числовое условие$3 > 100,BEGIN,END, или комбинация&&/|| - action -
{ ... }блок; если pattern без action - дефолт{ print }; если action без pattern - применяется ко всем строкам
Поля и встроенные переменные
| Переменная | Смысл |
|---|---|
$0 | вся строка |
$1, $2, ... $NF | поля 1..N |
NF | количество полей в текущей строке |
NR | номер текущей строки (общий счётчик) |
FNR | номер строки в текущем файле (для multi-file) |
FS | разделитель полей при чтении (default: пробелы/табы) |
OFS | разделитель при печати через print a,b,c |
RS | разделитель строк (default \n) |
ORS | разделитель строк при выводе |
FILENAME | имя текущего файла |
# Кто залогинен и какой shell
awk -F: '{ print $1, $7 }' /etc/passwd# Топ-10 IP по числу запросов в access.log
awk '{ print $1 }' access.log | sort | uniq -c | sort -rn | head# Сумма размеров файлов
ls -l | awk '{ sum += $5 } END { print sum/1024/1024 " MiB" }'BEGIN и END
BEGIN { ... }- до чтения первой строки. Часто: установкаFS, инициализация переменных, заголовок отчёта.END { ... }- после последней строки. Финальный summary.
awk 'BEGIN { FS=":"; print "user\tshell" } { print $1 "\t" $7 } END { print "total:", NR }' /etc/passwdУсловия и арифметика
# 5xx-запросы из nginx
awk '$9 >= 500 && $9 < 600' access.log
# SSH-логин-фейлы за сегодня
awk -v d="$(date +%b\ %d)" '$0 ~ d && /Failed password/' /var/log/auth.log
# Преобразовать байты в MiB в `ls -l`
awk '{ printf "%-30s %.2f MiB\n", $9, $5/1024/1024 }' <(ls -l)Awk строго типизирован между числом и строкой по контексту: $1 + 0
принудит к числу, $1 "" - к строке.
Ассоциативные массивы
Это главное что awk делает легче shell'а:
# Топ-5 IP-адресов по количеству 5xx
awk '$9 >= 500 { count[$1]++ } END { for (ip in count) print count[ip], ip }' access.log \| sort -rn | head -5
# Сумма bytes по user-agent
awk -F'"' '{ ua=$6; bytes[ua] += $NF } END { for (k in bytes) print bytes[k], k }' access.logПростой отчёт
# report.awk
BEGIN { FS=","; OFS="\t"; print "host", "errors", "avg_ms" }{ errs[$1] += $2; ms[$1] += $3; cnt[$1]++ }END { for (h in errs) print h, errs[h], ms[h]/cnt[h] }Запуск:
awk -f report.awk metrics.csv | sort -k2 -rn
awk vs sed vs jq
| Задача | Инструмент |
|---|---|
| Замена паттерна в строке | cmd-sed |
| Одна-две колонки + фильтр | awk |
| Агрегация по ключу | awk |
| JSON | [[cmd-jq |
| Многострочные структуры | Python |
| XML | XSLT / Python |
Когда что-то пошло не так
$10не работает - в awk$10это десятое поле, не первое + "0". Но вprint $1 0- конкатенация. Скобки решают:print $1, $1+10.- Поля сместились на пробелах в значениях - default FS =
[ \t]+жадный. Используй-F'\t'для строгих TSV илиawk -F'"' ...для CSV-with-quotes (но честный CSV-парсинг - Python). - Не находит pattern с
\d- awk POSIX не знает PCRE;\dне работает, пиши[0-9]. - gawk vs mawk -
gensub,asort, third arg вmatch()- GNU-only. Если скрипт ломается на Alpine (там mawk) - проверь. - Вход через stdin перепутался -
awk '{...}' < fileок,cat file | awkтоже ок, ноawk < file '{...}'- синтаксис.