# awk - обработка структурированного текста по полям _Команды · LinuxLab Knowledge Base_ **TL;DR:** awk бьёт строку на поля по FS (default - whitespace) и применяет pattern { action }. `$1..$NF`, `NR` (счётчик), BEGIN/END для пролога и итогов. Покрывает 80% задач "обработать колонки" без Python. ## Зачем 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 }' file awk -F: '{ print $1 }' /etc/passwd awk -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` | имя текущего файла | ```bash # Кто залогинен и какой 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. ```bash awk 'BEGIN { FS=":"; print "user\tshell" } { print $1 "\t" $7 } END { print "total:", NR }' /etc/passwd ``` ## Условия и арифметика ```bash # 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'а: ```bash # Топ-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 ``` ## Простой отчёт ```awk # 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] } ``` Запуск: ```bash awk -f report.awk metrics.csv | sort -k2 -rn ``` ## awk vs sed vs jq | Задача | Инструмент | |--------|------------| | Замена паттерна в строке | [cmd-sed](/kb/cmd-sed.md) | | Одна-две колонки + фильтр | awk | | Агрегация по ключу | awk | | JSON | [[cmd-jq|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 '{...}'` - синтаксис. ## Команды ```bash awk -F: '$3 >= 1000 { print $1 }' /etc/passwd ``` Пользователи с UID ≥ 1000 - обычно реальные люди, не systemd-юзеры ```bash awk '{ print $1 }' access.log | sort | uniq -c | sort -rn | head ``` Топ-10 клиентских IP в HTTP-логе - классический one-liner ```bash awk 'NR==FNR { a[$1]=1; next } !($1 in a)' allow.txt all.txt ``` Строки из all.txt, которых нет в allow.txt - diff по первому полю ```bash awk '/start/,/end/' file ``` Извлечь блок между маркерами - адресный диапазон ```bash awk 'END { print NR }' file ``` Подсчёт строк - быстрее `wc -l` ничем, но не плодит зависимостей ```bash ps -eo pid,user,rss,cmd | awk 'NR>1 { mem[$2] += $3 } END { for (u in mem) print mem[u]/1024 " MiB", u }' | sort -rn | head ``` Сколько RAM каждый юзер ест - из ps в один pipe ```bash awk -v t=$(date +%s) '$1 > t-3600' events.tsv ``` События за последний час - `-v` пробрасывает shell-переменную ## См. также - [sed - потоковый редактор текста](/kb/cmd-sed.md) - [grep - поиск строк по шаблону](/kb/cmd-grep.md) - [bash-скрипты - основы и идиомы](/kb/bash-scripting.md) - [jq - запросы и трансформация JSON](/kb/cmd-jq.md) - [xargs и find -exec - массовые операции](/kb/xargs-and-find-exec.md)