awk
Обработка текста по столбцам — выбор полей, фильтры, подсчёты и суммы
awk делит каждую строку на поля и выполняет для неё правила вида условие { действие }. Это небольшой язык с переменными, массивами, функциями и printf, поэтому на нём удобно выбирать столбцы, фильтровать строки по значению поля и считать итоги. В песочнице awk — это GNU awk (gawk) 5.2.
Синтаксис
awk [опции] 'программа' [файл…]
awk [опции] -f программа.awk [файл…]
Программа — набор правил условие { действие }. Для каждой строки ввода awk проверяет правила по порядку. Нет условия — действие выполняется для каждой строки. Нет действия — строка печатается целиком ({print}). Блок BEGIN { … } выполняется до чтения ввода, END { … } — после.
Опции
| Опция | Что делает |
|---|---|
-F разд | разделитель полей: символ (-F:), строка или регулярка (-F'[,;]'); -F'\t' — табуляция |
-v имя=значение | задать переменную до запуска (можно повторять) |
-f файл | взять программу из файла |
-e 'текст' | добавить кусок программы (вместе с -f) |
-i библиотека | подключить файл с функциями (например, -i inplace) |
-b | считать символы как байты |
-n | распознавать во входных данных восьмеричные (011) и шестнадцатеричные (0x1F) числа |
-M | точная арифметика с большими числами |
-P / -c | строгий режим POSIX / режим старого awk без расширений GNU |
-L | предупреждать о сомнительных и непереносимых конструкциях |
-d | после работы вывести все глобальные переменные в awkvars.out |
-o | красиво отформатировать программу в awkprof.out вместо запуска |
Присваивание можно написать и среди файлов: awk '…' x=1 file1 x=2 file2 — оно сработает перед чтением следующего файла.
Условия
| Условие | Когда срабатывает |
|---|---|
/рег/ | строка совпадает с регуляркой (ERE) |
$3 ~ /рег/ / $3 !~ /рег/ | поле совпадает / не совпадает |
$2 > 10, $1 == "x", NF != 3 | сравнение; &&, ||, ! — логика |
NR == 5, NR > 1 | по номеру строки |
усл1, усл2 | диапазон: от строки, где верно усл1, до строки, где верно усл2 |
BEGIN / END | до ввода / после ввода |
Встроенные переменные
| Переменная | Что значит |
|---|---|
$0 | вся текущая строка |
$1 … $NF | поля; $(NF-1) — предпоследнее |
NF | число полей в строке |
NR | номер строки, сквозной по всем файлам |
FNR | номер строки в текущем файле |
FILENAME | имя текущего файла |
FS / OFS | разделитель полей на входе (по умолчанию — пробелы и табуляции) / на выходе (пробел) |
RS / ORS | разделитель записей на входе / на выходе (по умолчанию \n); RS="" — записи разделены пустыми строками |
RSTART / RLENGTH | позиция и длина последнего совпадения match() |
ENVIRON["HOME"] | переменные окружения |
IGNORECASE=1 | сравнивать регулярки без учёта регистра (gawk) |
Язык
- Действия:
print a, b(черезOFS),printf "формат", a, b(как в C, без перевода строки в конце), присваивания= += -= ++,if (…) … else …,while,for (i = 1; i <= NF; i++),for (k in массив),next(к следующей строке),exit,delete массив[k]. - Массивы ассоциативные:
cnt[$1]++, проверка ключа —if ("x" in cnt). - Вывод можно перенаправить:
print > "файл",print >> "файл",print | "sort". - Строки склеиваются простым соседством:
x = $1 "-" $2.
Функции
| Функция | Что делает |
|---|---|
length(s) | длина строки (без аргумента — $0); для массива — число элементов |
substr(s, i, n) | подстрока с позиции i (с 1) длиной n |
index(s, t) | позиция t в s, 0 — если нет |
split(s, a, разд) | разбить s в массив a, вернуть число частей |
sub(/рег/, "з", s) / gsub(…) | заменить первое / все вхождения в s (по умолчанию в $0); & в замене — совпадение |
gensub(/рег/, "з", "g", s) | как gsub, но возвращает результат и понимает \\1 (gawk) |
match(s, /рег/, a) | найти совпадение, заполнить RSTART, RLENGTH и группы в a (gawk) |
tolower(s) / toupper(s) | регистр |
sprintf(формат, …) | как printf, но вернуть строку |
int(x), sqrt(x), rand() | целая часть, корень, случайное число |
system("команда") | запустить команду оболочки |
strftime("%F", t), systime() | дата и текущее время (gawk) |
asort(a), asorti(a) | отсортировать массив по значениям / по ключам (gawk) |
Примеры
printf 'alice 30 dev\nbob 25 ops\ncarol 35 dev\n' > people.txt
# первый и последний столбец
awk '{print $1, $NF}' people.txt
# фильтр по значению поля и по регулярке
awk '$3 == "dev" {print $1, $2}' people.txt
awk '$2 > 28' people.txt
awk '$1 ~ /^c/' people.txt
# пропустить первую строку (заголовок)
awk 'NR > 1' people.txt
# сумма, среднее, максимум
awk '{s += $2} END {print s, s / NR}' people.txt
awk 'NR == 1 || $2 > max {max = $2} END {print max}' people.txt
# подсчёт по группам (ассоциативный массив)
awk '{c[$3]++} END {for (k in c) print k, c[k]}' people.txt
# сумма по группам
awk '{s[$3] += $2} END {for (k in s) print k, s[k]}' people.txt
# первая строка для каждого значения поля — дубли отбрасываются
awk '!seen[$3]++' people.txt
# другой разделитель на входе и на выходе
echo 'a:b:c' | awk -F: -v OFS=- '{$1 = $1; print}'
printf 'id,name\n1,alice\n' | awk -F, '{print $2}'
# форматированный вывод
awk '{printf "%-6s %3d\n", $1, $2}' people.txt
# передать значение из оболочки
min=30
awk -v min="$min" '$2 >= min {print $1}' people.txt
# поменять столбцы местами
awk '{print $2, $1}' people.txt
# замена внутри поля и вывод
awk '{gsub(/a/, "A", $1); print $1}' people.txt
# группа регулярки (gawk): вытащить число из строки
echo 'took 125ms' | awk 'match($0, /([0-9]+)ms/, m) {print m[1]}'
# строки, где не 3 поля, — с номерами
printf 'a b c\nd e\n' | awk 'NF != 3 {print NR": "$0}'
# объединить два файла по первому полю: сначала читаем первый в массив
printf 'alice admin\nbob user\n' > roles.txt
awk 'FNR == NR {role[$1] = $2; next} {print $1, role[$1]}' roles.txt people.txt
Подводные камни
- Программу пиши в одинарных кавычках. В двойных оболочка подставит
$1сама (обычно пустоту), иawk "{print $1}"напечатает строки целиком. - Переменные оболочки внутри
'…'не видны. Передавай их через-v, а не склеивая кавычки. - Строка или число.
$1 > "9"— сравнение строк, и10окажется «меньше»9. Сравнивай с числом без кавычек:$1 > 9. - По умолчанию разделитель — любая цепочка пробелов и табуляций, а пробелы в начале строки игнорируются (
-F' 'означает то же самое). С любым другим разделителем пустые поля сохраняются: с-F,вa,,bтри поля, и$3— этоb; с-F'\t'две табуляции подряд тоже дают пустое поле. OFSприменяется, только когда строка пересобирается.printбез изменений выведет$0как было; присвой любое поле ($1 = $1), чтобы строка собралась заново с новым разделителем.print $1 $2склеивает поля без пробела. Запятая (print $1, $2) вставляетOFS.printfне добавляет перевод строки — пиши\nв формате.- Порядок в
for (k in массив)не определён. Если нужен порядок — отсортируй вывод через sort. - Обратных ссылок
\1в регулярках awk нет. Для групп используйmatch(…, …, массив)илиgensub. - Локаль. В песочнице локаль
POSIX, поэтомуlength()иsubstr()считают байты: упрдлина 4, аtoupperне трогает кириллицу. СLC_ALL=C.utf8считаются символы.