LEARN X · ЗА 12 МИН
AWK
AWK за 12 минут: модель pattern { action }, поля и записи, BEGIN/END, переменные FS/OFS/NR/NF, массивы, строковые функции и однострочники.
AWK — это язык и утилита для построчной обработки текста, разбитого на поля. Программа AWK — это набор правил вида pattern { action }: для каждой строки входа AWK проверяет паттерны и выполняет действия для совпавших. Идеально подходит для отчётов, логов и табличных данных. Весь язык — ниже, в комментариях кода.
Модель работы
Программа — это правила pattern { action }. AWK читает вход построчно (запись за записью) и применяет каждое правило по очереди.
# Комментарии начинаются с # и идут до конца строки
# Базовая единица программы — правило:
# pattern { action }
# pattern — условие (когда выполнять); можно опустить => всегда
# action — что делать в { ... }; можно опустить => print $0
/error/ { print "нашёл ошибку" } # есть pattern и action
$3 > 100 # только pattern => печать строки
{ print "любая строка" } # только action => для всех строк
# AWK сам крутит главный цикл по строкам:
# читать строку -> проверить все правила -> следующая строка
Поля и записи
Каждая строка — запись, она автоматически делится на поля по пробелам/табам.
# Дано строка: alice 30 engineer
$0 # вся запись целиком: "alice 30 engineer"
$1 # первое поле: "alice"
$2 # второе поле: "30"
$3 # третье поле: "engineer"
NF # Number of Fields — число полей в строке (здесь 3)
$NF # последнее поле (поле с номером NF): "engineer"
NR # Number of Record — номер текущей строки (1, 2, 3, ...)
{ print NR, $1, $NF } # "1 alice engineer"
# Полям можно присваивать — строка пересобирается:
{ $2 = $2 + 1; print } # увеличили возраст на 1 и напечатали
{ $4 = "new"; print } # добавили 4-е поле (NF станет 4)
Запуск
# Программа прямо в командной строке (в одинарных кавычках):
awk '{ print $1 }' file.txt
# Несколько файлов подряд:
awk '{ print }' a.txt b.txt
# Из stdin по конвейеру:
cat /etc/passwd | awk -F: '{ print $1 }'
# -F задаёт разделитель полей (Field Separator):
awk -F: '{ print $1 }' /etc/passwd # делить по двоеточию
awk -F',' '{ print $2 }' data.csv # делить по запятой (CSV)
awk -F'\t' '{ print $1 }' data.tsv # делить по табу
# Передать переменную снаружи через -v:
awk -v limit=100 '$1 > limit' data.txt
# Программа из файла через -f:
awk -f script.awk data.txt
Вывод
# print — простая печать, аргументы через запятую разделяются OFS (по умолч. пробел)
{ print $1, $2 } # "alice 30"
{ print $1 $2 } # без запятой => склейка: "alice30"
{ print "hi", NR } # текст и значения вперемешку
{ print } # то же что print $0
# printf — форматированный вывод (как в C), сам перевод строки НЕ ставит
{ printf "%s\n", $1 } # строка + явный \n
{ printf "%-10s %5d\n", $1, $2 } # выравнивание: влево 10, вправо 5
{ printf "%.2f\n", $3 } # два знака после точки
{ printf "%d%%\n", 50 } # %% => литеральный знак процента
# Перенаправление вывода в файл прямо из AWK:
{ print $1 > "names.txt" } # перезапись/запись в файл
{ print $1 >> "log.txt" } # дозапись в конец
Паттерны
# Регулярное выражение /regex/ — совпадение по всей строке $0:
/error/ { print } # строки, содержащие "error"
/^#/ { next } # строки-комментарии пропустить
/[0-9]+/ { print } # строки с цифрами
# Совпадение по конкретному полю — операторы ~ и !~ :
$1 ~ /^a/ { print } # 1-е поле начинается с 'a'
$2 !~ /^[0-9]/ { print } # 2-е поле НЕ начинается с цифры
# Любое логическое выражение как паттерн:
$3 > 100 { print } # сравнение чисел
NF == 0 # пустые строки (нет полей)
$1 == "alice" { print } # точное равенство строки
NR % 2 == 0 { print } # каждая чётная строка
# Диапазон строк: /начало/,/конец/ — от первого совпадения до второго
/START/,/END/ { print } # блок между маркерами включительно
BEGIN и END
Особые паттерны: BEGIN — до чтения входа, END — после всех строк.
BEGIN {
# выполняется ОДИН раз до первой строки
print "=== Отчёт ===" # шапка
FS = "," # настроить разделитель здесь тоже можно
sum = 0 # инициализация переменных
}
{ sum += $1 } # тело: для каждой строки
END {
# выполняется ОДИН раз после последней строки
print "Сумма:", sum
print "Строк:", NR # NR в END = общее число строк
}
# Только BEGIN, без чтения файла — AWK как калькулятор:
# awk 'BEGIN { print 2 + 2 * 3 }' => 8
Переменные
# Встроенные переменные (можно читать и менять):
# FS — Field Separator, разделитель ВХОДНЫХ полей (по умолч. пробел/таб)
# OFS — Output FS, разделитель полей при print (по умолч. пробел)
# RS — Record Separator, разделитель записей (по умолч. перевод строки)
# ORS — Output RS, что печатать после print (по умолч. \n)
# NR, NF — номер записи и число полей (см. выше)
# FILENAME — имя текущего файла
BEGIN { FS = ":"; OFS = " -> " }
{ print $1, $3 } # поля войдут по ':', выйдут через ' -> '
BEGIN { RS = "" } # пустая RS => абзацы как записи
BEGIN { ORS = "; " } # печатать всё в одну строку через '; '
# Пользовательские переменные — без объявления, тип динамический:
{ count = count + 1 } # число (неинициализир. = 0 или "")
{ name = $1 } # строка
{ total += $2 } # += короткая форма
Операторы и условия
# Арифметика: + - * / % ^(степень)
{ x = 2 ^ 10 } # 1024
{ r = 17 % 5 } # остаток = 2
# Сравнения: == != < <= > >=
# Логика: && (и) || (или) ! (не)
{
if ($2 >= 18) {
print $1, "взрослый"
} else if ($2 >= 12) {
print $1, "подросток"
} else {
print $1, "ребёнок"
}
}
# Тернарный оператор: условие ? если_да : если_нет
{ print $1, ($2 >= 18 ? "18+" : "<18") }
# Конкатенация строк — просто рядом, без оператора:
{ full = $1 " " $2 } # объединение через пробел
# next — перейти к следующей строке; exit — завершить программу
/^#/ { next } # пропустить комментарии
$1 == "STOP" { exit } # выйти досрочно (END всё равно сработает)
Циклы
# for — классический счётчик:
{
for (i = 1; i <= NF; i++) { # пройти по всем полям строки
print i, $i
}
}
# while — пока условие истинно:
BEGIN {
i = 1
while (i <= 5) {
print i
i++
}
}
# do-while — тело хотя бы раз:
BEGIN {
i = 0
do { print i; i++ } while (i < 3)
}
# Управление циклом:
# break — выйти из цикла
# continue — к следующей итерации
{
for (i = 1; i <= NF; i++) {
if ($i == "") continue
if ($i == "END") break
print $i
}
}
Массивы
Массивы в AWK ассоциативные: ключ — любая строка или число.
# Присваивание создаёт элемент:
{ count[$1]++ } # счётчик вхождений по значению 1-го поля
{ sum[$1] += $2 } # сумма по группам (ключ = группа)
{ seen[$0] = 1 } # отметка "видели такую строку"
# Обход массива: for (ключ in массив) — порядок НЕ гарантирован
END {
for (key in count) {
print key, count[key]
}
}
# Проверка наличия ключа: (ключ in массив)
END {
if ("alice" in count) print "alice есть"
}
# Удаление элемента:
END { delete count["alice"] } # один элемент
END { delete count } # весь массив
# Многомерность через SUBSEP (склейка ключей):
{ grid[$1, $2] = $3 } # grid["a", "b"] на деле "a\034b"
# Длина массива — число ключей:
END { print length(count) }
Строковые функции
# length(s) — длина строки (или массива)
{ print length($1) }
{ print length } # длина всей записи $0
# substr(s, start, len) — подстрока; индексы с 1
{ print substr($1, 1, 3) } # первые 3 символа
{ print substr($1, 2) } # с 2-го символа до конца
# index(s, sub) — позиция подстроки (0 если нет)
{ print index($1, "oo") }
# split(s, arr, sep) — разбить строку в массив, вернуть число частей
{ n = split($0, parts, ","); print n, parts[1] }
# sub(re, repl, target) — заменить ПЕРВОЕ совпадение (меняет target)
{ sub(/old/, "new"); print } # в $0
{ sub(/-/, "_", $1); print $1 } # в конкретном поле
# gsub(re, repl, target) — заменить ВСЕ совпадения, вернуть их число
{ n = gsub(/a/, "A"); print n, $0 } # сколько заменили + результат
# match(s, re) — есть ли совпадение; ставит RSTART и RLENGTH
{ if (match($0, /[0-9]+/)) print substr($0, RSTART, RLENGTH) }
# toupper / tolower — регистр
{ print toupper($1), tolower($2) }
# sprintf(fmt, ...) — как printf, но ВОЗВРАЩАЕТ строку (не печатает)
{ id = sprintf("%04d", NR); print id, $1 } # "0001 alice"
Практические однострочники
# Сумма колонки (3-е поле):
awk '{ s += $3 } END { print s }' data.txt
# Среднее по колонке:
awk '{ s += $1 } END { print s / NR }' nums.txt
# Печать только нужных колонок:
awk '{ print $1, $3 }' data.txt
# Фильтрация по условию (зарплата > 1000):
awk '$2 > 1000' salaries.txt
# Подсчёт строк (аналог wc -l):
awk 'END { print NR }' file.txt
# Уникальные значения с подсчётом (аналог sort | uniq -c):
awk '{ c[$1]++ } END { for (k in c) print c[k], k }' log.txt
# Убрать дубликаты строк, сохранив порядок:
awk '!seen[$0]++' file.txt
# Печать строк длиннее 80 символов:
awk 'length > 80' file.txt
# Нумерация непустых строк:
awk 'NF { print ++n, $0 }' file.txt
# Поменять местами два первых поля:
awk '{ t = $1; $1 = $2; $2 = t; print }' data.txt
# Сумма по группам (ключ — 1-е поле, значение — 2-е):
awk '{ g[$1] += $2 } END { for (k in g) print k, g[k] }' data.txt