Шпаргалка AWK
AWK - мощный язык обработки текста и потоковых данных, особенно удобен для работы с колонками в таблицах (CSV, лог-файлы). Назван по фамилиям авторов: Aho, Weinberger, Kernighan.
Основной синтаксис
# Базовые варианты запуска
awk 'программа' файл # Простая программа
awk -F':' 'программа' файл # С разделителем полей
awk -f скрипт.awk файл # Программа из файла
awk -v var=значение 'программа' файл # С переменной
# Реальные примеры
awk '{print $1}' /etc/passwd # Имена пользователей
awk -F':' '{print $1}' /etc/passwd # Поля через :
awk -f analyze.awk data.txt # Запуск скрипта
awk -v min=100 '$3 > min' sales.txt # Фильтр с переменной
Структура программы
# Общая структура
BEGIN { действия до обработки }
условие { действия для строк, удовлетворяющих условию }
/регулярка/ { действия для строк, совпадающих с regexp }
END { действия после обработки }
# Реальные примеры
awk 'BEGIN {print "=== НАЧАЛО ОТЧЁТА ==="}
$3 > 1000 {print $1, $3}
END {print "=== КОНЕЦ ОТЧЁТА ==="}' data.txt
# Специальные правила
awk '/error/ {print "Ошибка в строке", NR}' log.txt # Поиск ошибок
awk '$1 == "END" {next} {print}' data.txt # Пропуск строк с END
awk 'NF < 3 {print "Короткая строка:", $0}' data.txt # Строки с <3 полями
Специальные переменные
Основные переменные
$0- вся текущая строкаawk '{print $0}' file.txt # печать всех строк$1,$2, ...,$NF- поля строкиawk '{print $1, $3}' data.txt # 1-е и 3-е поле awk '{print $NF}' data.txt # последнее полеNF- количество полей в строкеawk '{print NF, $0}' data.txt # показать число полейNR- номер текущей строки (общий для всех файлов)awk 'NR==10' file.txt # 10-я строка awk 'END{print NR}' file.txt # всего строкFNR- номер строки в текущем файлеawk 'FNR==1' file1.txt file2.txt # первые строки каждого файлаFILENAME- имя текущего файлаawk '{print FILENAME, $0}' *.txt # показать имя файла для каждой строки
Разделители
FS- разделитель полей на входе (field separator)awk 'BEGIN{FS=","} {print $1}' data.csvOFS- разделитель полей на выходе (output field separator)awk 'BEGIN{OFS=";"} {$1=$1; print}' data.txtRS- разделитель записей (record separator)awk 'BEGIN{RS=""} {print "Абзац:", $0}' text.txtORS- разделитель записей на выходеawk 'BEGIN{ORS="\n\n"} {print}' file.txt # двойной интервал
Дополнительные
ARGC,ARGV- аргументы командной строкиawk 'BEGIN{for(i=0;i<ARGC;i++) print ARGV[i]}' a b cIGNORECASE- игнорировать регистр (1 = да, 0 = нет)awk 'BEGIN{IGNORECASE=1} /error/' log.txt
Управление потоком
next - пропустить остальные правила
# Пропустить комментарии и пустые строки
awk '/^#/ {next} /^$/ {next} {print "Данные:", $0}' config.txt
exit - завершить выполнение
# Остановиться после нахождения критической ошибки
awk '/FATAL/ {print "Критическая ошибка!"; exit} {print}' log.txt
# exit с кодом возврата
awk 'BEGIN{print "Ошибка" > "/dev/stderr"; exit 1}'
nextfile - перейти к следующему файлу (gawk)
# Обработать только первые 5 строк каждого файла
gawk 'FNR > 5 {nextfile} {print FILENAME ":", $0}' *.txt
Модификация полей
При изменении поля AWK пересобирает $0 с использованием OFS:
# Сделать имена заглавными
awk '{$1 = toupper($1); print}' users.txt
# Удалить второе поле
awk '{$2 = ""; print}' contacts.txt
# Оставить только имя и зарплату
awk '{$0 = $1 " " $3; print}' employees.txt
# Поменять местами поля
awk '{temp = $1; $1 = $2; $2 = temp; print}' data.txt
# Добавить префикс
awk '{$0 = "ID: " $0; print}' data.txt
Работа с файлами
Обработка двух файлов
# Ключевой паттерн: FNR==NR для первого файла
# Добавить отделы к сотрудникам
# employees.txt: 101 Иван 50000
# departments.txt: 101 IT
awk 'FNR==NR {dept[$1]=$2; next}
$1 in dept {print $0, dept[$1]}' departments.txt employees.txt
# Вывод: 101 Иван 50000 IT
# Найти строки, общие для двух файлов
awk 'NR==FNR {a[$0]; next} $0 in a' file1.txt file2.txt
# Найти строки, уникальные для file1
awk 'NR==FNR {a[$0]; next} !($0 in a)' file2.txt file1.txt
Разделение на файлы
# Разделить по значению поля
awk '{print > $1 ".txt"}' data.txt
# Разделить по условию
awk '{if($3 > 1000) print > "high.txt"; else print > "low.txt"}' sales.txt
Строковые функции
Основные строковые функции
length([строка])- длина строкиawk '{print length($0)}' file.txt # длина каждой строки awk 'length > 80' file.txt # строки длиннее 80 символовsubstr(строка, начало [, длина])- подстрокаawk '{print substr($1, 1, 3)}' data.txt # первые 3 символа awk '{print substr($0, length-5)}' file # последние 5 символовindex(строка, подстрока)- позиция подстрокиawk '{if(index($0, "ERROR") > 0) print}' log.txtsplit(строка, массив [, разделитель])- разбить строкуawk '{split($0, arr, ":"); print arr[1], arr[3]}' /etc/passwd
Замена и регистр
gsub(regexp, замена [, строка])- глобальная заменаawk '{gsub(/[0-9]/, "X"); print}' file.txt # цифры → X awk '{gsub(/\s+/, " "); print}' file.txt # сжать пробелыsub(регулярка, замена [, строка])- замена первого вхожденияawk '{sub(/^/, "> "); print}' file.txt # добавить в начало awk '{sub(/\s+$/, ""); print}' file.txt # удалить пробелы в концеtoupper(строка)/tolower(строка)- регистрawk '{print toupper($1)}' names.txt # ЗАГЛАВНЫЕ awk '{print tolower($1)}' names.txt # строчныеmatch(строка, регулярка [, массив])- поиск совпаденияawk '{if(match($0, /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/)) print "IP найден"}' log.txtsprintf(формат, значения)- форматированиеawk '{sum+=$2} END{print sprintf("Среднее: %.2f", sum/NR)}' data.txt
Математические функции
Базовые
int(x)- целая частьawk '{print int($1)}' data.txt # отбросить дробную частьsqrt(x)- квадратный кореньawk '{print "Корень из", $1, "=", sqrt($1)}' numbers.txtexp(x),log(x)- экспонента и натуральный логарифмawk '{print exp($1)}' data.txt
Тригонометрия и случайные числа
sin(x),cos(x),atan2(y, x)- тригонометрияrand()- случайное число от 0 до 1awk 'BEGIN{srand(); print int(rand()*100)}' # число от 0 до 99srand([seed])- инициализация генератораawk 'BEGIN{srand(); for(i=0;i<5;i++) print rand()}'
Временные функции (gawk)
# Текущее время
gawk 'BEGIN{print "Сейчас:", strftime("%Y-%m-%d %H:%M:%S")}'
# Конвертация даты в timestamp
gawk '{timestamp = mktime($1" " $2" " $3" 00 00 00")}' dates.txt
Массивы
Ассоциативные массивы
# Подсчёт частоты слов
awk '{ for(i=1; i<=NF; i++) count[$i]++ }
END { for(word in count) print count[word], word }' text.txt | sort -rn
# Группировка и суммирование
awk '{ sales[$1] += $3; count[$1]++ }
END { for(dept in sales) print dept, sales[dept], sales[dept]/count[dept] }' sales.txt
# Проверка существования ключа
awk '{if("key" in arr) print "существует"}'
# Удаление элемента
awk '{delete arr["key"]}'
# Удаление всего массива (gawk)
gawk '{delete arr}'
Обход массива
# Порядок не гарантирован
awk '{ data[$1] = $2 } END { for(key in data) print key, data[key] }'
# Сортировка ключей (через внешнюю сортировку)
awk '{data[$1]=$2} END{for(k in data) print k, data[k]}' | sort
Ввод/вывод
getline - явное чтение
# Чтение из другого файла
awk '{ getline name < "names.txt"; print name, $0 }' data.txt
# Чтение из команды
awk 'BEGIN{ "date +%Y-%m-%d" | getline today; print "Сегодня:", today; close("date") }'
# Чтение нескольких строк
awk '{ while(getline line < "data.txt") print "Чтение:", line; close("data.txt") }'
Перенаправление вывода
# Запись в файл (> перезаписывает)
awk '{print $1 > "output.txt"}' data.txt
# Дозапись в файл (>> добавляет)
awk '{print $2 >> "all.txt"}' data.txt
# Отправка в pipe
awk '{print $0 | "sort -rn > sorted.txt"}' data.txt
# Закрытие файлов (важно при многократном открытии)
awk '{
file = $1 ".txt"
print $2 > file
close(file)
}'
Вывод на экран
# В stderr
awk '{print "Ошибка в строке", NR > "/dev/stderr"}'
# Форматированный вывод
awk '{printf "%-20s %10.2f\n", $1, $2}' data.txt
Практические однострочники
Фильтрация строк
# Пропустить заголовок (первую строку)
awk 'NR > 1' data.txt
# Строки с 10 по 20
awk 'NR >= 10 && NR <= 20' file.txt
# Каждую 3-ю строку
awk 'NR % 3 == 0' file.txt
# Непустые строки
awk 'NF > 0' file.txt
# Строки, начинающиеся с цифры
awk '/^[0-9]/' file.txt
# Строки без цифр
awk '!/[0-9]/' file.txt
# Строки длиной ровно 80 символов
awk 'length == 80' file.txt
Работа с полями
# Вывести определённые столбцы
awk '{print $1, $3, $NF}' data.txt
# Поменять местами столбцы
awk '{print $2, $1}' names.txt
# Удалить первый столбец
awk '{$1=""; sub(/^ /, ""); print}' data.txt
# Удалить последний столбец
awk '{NF-; print}' data.txt
# Суммировать столбец
awk '{sum += $2} END {print "Сумма:", sum}' data.txt
# Среднее по столбцу
awk '{sum += $2; count++} END {print "Среднее:", sum/count}' data.txt
Поиск и замена
# Замена текста во всех полях
awk '{gsub(/foo/, "bar"); print}' file.txt
# Замена только в первом поле
awk '{gsub(/foo/, "bar", $1); print}' file.txt
# Удалить все цифры
awk '{gsub(/[0-9]/, ""); print}' file.txt
# Оставить только цифры
awk '{gsub(/[^0-9]/, ""); print}' file.txt
# Удалить пробелы в начале и конце
awk '{gsub(/^[ \t]+|[ \t]+$/, ""); print}' file.txt
# Сжать множественные пробелы
awk '{$1=$1; print}' file.txt
# Сменить разделитель (табы → запятые)
awk '{$1=$1; print}' OFS=',' file.txt
Статистика
# Минимальное значение
awk 'NR==1 || $1 < min {min=$1} END {print "Минимум:", min}' data.txt
# Максимальное значение
awk '$1 > max {max=$1} END {print "Максимум:", max}' data.txt
# Размах (max - min)
awk 'NR==1 {min=max=$1} {if($1<min)min=$1; if($1>max)max=$1}
END {print "Размах:", max-min}' data.txt
# Сумма по группам
awk '{sum[$1] += $2} END {for(g in sum) print g, sum[g]}' data.txt
# Количество уникальных значений
awk '!seen[$1]++ {count++} END {print "Уникальных:", count}' data.txt
Работа с дубликатами
# Уникальные строки (первое вхождение)
awk '!seen[$0]++' duplicates.txt
# Только дубликаты
awk 'seen[$0]++' duplicates.txt | sort -u
# Подсчёт дубликатов
awk '{count[$0]++} END {for(line in count) print count[line], line}' | sort -rn
# Уникальные по первому полю
awk '!seen[$1]++' data.txt
Форматирование
# Выравнивание по левому краю
awk '{printf "%-20s %-10s\n", $1, $2}' data.txt
# Выравнивание по правому краю
awk '{printf "%20s %10s\n", $1, $2}' data.txt
# Числа с двумя знаками после запятой
awk '{printf "%.2f\n", $1}' data.txt
# Добавить ведущие нули (ID: 00001)
awk '{printf "ID: %05d\n", $1}' ids.txt
# CSV с кавычками
awk '{printf "\"%s\",\"%s\"\n", $1, $2}' data.txt > output.csv
# Простой JSON
awk '{printf "{\"name\":\"%s\",\"value\":%s}\n", $1, $2}' data.txt
Обработка CSV
# CSV → TSV (табы)
awk -F',' 'BEGIN{OFS="\t"} {$1=$1; print}' data.csv
# TSV → CSV
awk 'BEGIN{FS=OFS="\t"} {$1=$1; print}' OFS=',' data.tsv
# Извлечь столбец по заголовку
awk -F',' 'NR==1{for(i=1;i<=NF;i++) if($i=="Email") col=i} NR>1{print $col}' users.csv
# Проверка целостности (все строки должны иметь одинаковое число полей)
awk -F',' 'NR==1{cols=NF} NF!=cols{print "Ошибка в строке", NR}' data.csv
Обработка логов
# Топ-10 IP-адресов
awk '{ip[$1]++} END{for(i in ip) print ip[i], i}' access.log | sort -rn | head -10
# Подсчёт HTTP статусов
awk '{status[$9]++} END{for(s in status) print s, status[s]}' access.log
# Средний размер ответа
awk '{sum+=$10; count++} END{print "Средний:", sum/count}' access.log
# Ошибки 404
awk '$9 == 404 {print}' access.log
# Запросы за определённый час
awk -F'[][]' '$2 ~ /09\/Mar\/2024:14/' access.log
Преобразование данных
# Развернуть строку в столбец (транспонировать)
echo "a b c" | awk '{for(i=1;i<=NF;i++) print $i}'
# Свернуть столбец в строку
awk '{printf "%s%s", sep, $0; sep=","} END{print ""}' column.txt
# Добавить номер строки
awk '{printf "%3d: %s\n", NR, $0}' data.txt
# Зеркальное отражение полей
awk '{for(i=NF;i>0;i-) printf "%s ", $i; print ""}' data.txt
# Группировка по 3 строки в одну
awk '{printf "%s%s", (NR%3==1 ? "" : " "), $0}
NR%3==0 {print ""}
END {if(NR%3!=0) print ""}' data.txt
Продвинутые примеры
Условное форматирование
# Выделить значения выше порога
awk '{
if($2 > 100)
printf "%-15s %6.2f ***\n", $1, $2
else
printf "%-15s %6.2f\n", $1, $2
}' data.txt
# Цветной вывод (ANSI-коды)
awk '{
if($2 > 100)
printf "\033[31m%s %s\033[0m\n", $1, $2 # красный
else
print $1, $2
}' data.txt
Скользящее окно
# Анализ тренда (последние 3 значения)
awk '{
prices[NR] = $2
}
NR >= 3 {
if(prices[NR] > prices[NR-1] && prices[NR-1] > prices[NR-2])
trend = "↑↑"
else if(prices[NR] < prices[NR-1] && prices[NR-1] < prices[NR-2])
trend = "↓↓"
else
trend = "→"
printf "Строка %d: %s %s %s\n", NR, $1, $2, trend
}' stock.txt
Сравнение с предыдущей строкой
# Поиск резких изменений
awk 'NR > 1 {
diff = $2 - prev_temp
if(diff > 10)
print "Резкое повышение на", NR, ":", prev, "→", $0
else if(diff < -10)
print "Резкое понижение на", NR, ":", prev, "→", $0
}
{
prev = $0
prev_temp = $2
}' temperatures.txt
Многомерные данные
# Эмуляция двумерного массива
awk '{
# ключ: отдел_месяц
sales[$1, $2] += $3
}
END {
for(comb in sales) {
split(comb, keys, SUBSEP)
print keys[1], keys[2], sales[comb]
}
}' sales.txt
# Таблица по отделам и месяцам
awk '{
dept = $1
month = $2
amount = $3
depts[dept]
months[month]
sales[dept, month] += amount
}
END {
# Заголовок
printf "Отдел"
for(m in months)
printf "\t%s", m
print ""
# Данные
for(d in depts) {
printf "%s", d
for(m in months)
printf "\t%.0f", sales[d, m]
print ""
}
}' sales.txt
Генерация отчётов
# Отчёт с итогами
awk 'BEGIN {
print "================================="
print "ОТЧЁТ О ПРОДАЖАХ"
print "================================="
print sprintf("%-15s %10s %10s", "Регион", "Продажи", "Сделок")
print "-"
}
{
sales[$1] += $3
count[$1]++
total_sales += $3
total_count++
}
END {
for(region in sales) {
printf "%-15s %10.2f %10d\n", region, sales[region], count[region]
}
print "-"
printf "%-15s %10.2f %10d\n", "ИТОГО:", total_sales, total_count
print "================================="
}' sales.txt
Отладка
Печать отладочной информации
# В stderr
awk '{print "DEBUG: NR=" NR, "$0=" $0 > "/dev/stderr"; print $0}' data.txt
# Условная отладка
awk '{
if(DEBUG) print "DEBUG:", $0 > "/dev/stderr"
# основной код
}' DEBUG=1 data.txt
Профилирование (gawk)
# Профилирование скрипта
gawk -profile=profile.out -f script.awk data.txt
# Просмотр результатов
head -20 profile.out
Отладчик gawk
# Запуск в режиме отладки
gawk -D -f script.awk data.txt
# Команды отладчика:
# breakpoint N - остановка на строке N
# watch var - слежение за переменной
# step - шаг с заходом
# next - шаг без захода
# print var - печать значения
# continue - продолжить
# quit - выход
Ключевые паттерны для запоминания
# Уникальные строки
awk '!seen[$0]++' file.txt
# Обработка двух файлов
awk 'NR==FNR {a[$1]; next} $1 in a' file1 file2
# Нормализация пробелов
awk '{$1=$1; print}' file.txt
# Непустые строки
awk 'NF > 0' file.txt
# Пропустить заголовок
awk 'NR > 1' file.txt
# Подсчёт строк
awk 'END {print NR}' file.txt
# Сумма столбца
awk '{sum += $1} END {print sum}' file.txt
# Среднее арифметическое
awk '{sum += $1} END {print sum/NR}' file.txt
# Максимальное значение и строка с ним
awk '$1 > max {max = $1; line = $0} END {print line}' file.txt
# Частота слов
awk '{for(i=1;i<=NF;i++) freq[$i]++} END {for(w in freq) print freq[w], w}' | sort -rn
# CSV в TSV
awk -F',' 'BEGIN{OFS="\t"} {$1=$1; print}' file.csv
# Фильтр по условию
awk '$3 > 100 && $3 < 1000' file.txt
# Замена во всей строке
awk '{gsub(/old/, "new"); print}' file.txt
# Вывод с номером строки
awk '{print NR ":", $0}' file.txt
# Сумма по группам
awk '{sum[$1] += $2} END {for(g in sum) print g, sum[g]}' file.txt
Полезные советы
Эффективность
Используйте
nextдля раннего выхода из правил/^#/ {print "Комментарий"; next} # пропустить остальные правила {print "Данные:", $0}Закрывайте файлы при многократной записи
{print > $1 ".txt"; close($1 ".txt")}Избегайте
getlineкогда можно обойтись без него# Вместо getline используйте обработку двух файлов awk 'NR==FNR {a[NR]=$0; next} {print $0, a[FNR]}' file2 file1
Часто используемые регулярки
/^[0-9]/ # начинается с цифры
/[0-9]$/ # заканчивается цифрой
/^[A-Z]/ # начинается с заглавной буквы
/^[[:space:]]*$/ # пустая строка (только пробелы)
/[0-9]+\.[0-9]+/ # число с плавающей точкой
/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/ # IP-адрес
Переменные окружения
# Доступ к переменным окружения
awk 'BEGIN{print "HOME:", ENVIRON["HOME"]}'
awk 'BEGIN{print "USER:", ENVIRON["USER"]}'
# Передача переменных из командной строки
awk -v threshold=100 '$3 > threshold' data.txt