Найти в Дзене
Изучаем Linux

AWK

Linux поддерживает массу языков программирования общего назначения: C, C++, Java, Perl, PHP, Python, ... Но есть и языки программирования, специализированные для определенной цели. Обычно это интерпретируемые языки с усечённым синтаксисом. И язык AWK первоначально предназначался для решения узкого класса задач и его можно было отнести к таким мини-языкам, но поздние версии (в том числе GAWK)

Linux поддерживает массу языков программирования общего назначения: C, C++, Java, Perl, PHP, Python, ... Но есть и языки программирования, специализированные для определенной цели. Обычно это интерпретируемые языки с усечённым синтаксисом. И язык AWK первоначально предназначался для решения узкого класса задач и его можно было отнести к таким мини-языкам, но поздние версии (в том числе GAWK) расширили его границы.

Скриптовый язык AWK назван по первым буквам фамилий его создателей: Альфреда Ахо, Питера Вайнербергера и Брайана Кернигана. GNU-вариант языка имеет название GAWK и существенно расширен. AWK прекрасно подходит для обработки данных, разделенных на строки, в каждой из которых есть поля (например, данные, разбитые на строки и столбцы), в том числе AWK обрабатывает и переформатирует вывод какой-то другой программы. В классическом смысле, AWK - это фильтр UNIX/Linux, т.к. он считывает исходный файл, если он указан (либо стандартный ввод, если файл не указан), и его можно использовать в каналах. Основная задача AWK - строка за строкой читать входные данные и разбивать каждую строку на поля.

Через следующие обозначения (внутренние переменные AWK) производится доступ к полям строки:
$0 - вся строка
$1 - поле 1
$2 - поле 2
$3 - поле 3
..................

В общем случае программа на языке AWK состоит из набора инструкций, каждая из которых может включать шаблон и некоторые действия (одно или несколько). Общий вид инструкции:

template { action_1; action_2; ... }

На самом деле эти правила очень гибкие. Программа может состоять всего из одной инструкции - это однострочная программа AWK, и такие очень часто встречаются и находят применение, в том числе для скриптов системного назначения в операционной системе. В предложении выше было сказано, что каждая из инструкций может включать шаблон и действия - здесь ключевым является слово "может". Дело в том, что шаблон позволяет выбрать из входного потока подмножество строк, над которыми будут применены действия (форматирование, обработка), если они указаны вслед за шаблоном в фигурных скобках. Если же действия вслед за шаблоном не указаны, то подмножество строк просто отправляется на вывод программы необработанными. И наоборот: программа может содержать только действия - и они применяются ко всем строкам входного потока.

Примеры шаблонов:

BEGIN - срабатывает до считывания первой строки ввода

END - срабатывает после считывания последней строки ввода

$1=="root" - пример выражения (первое поле содержит строку "root")

/^[0-9]/ - любая строка, начинающаяся с цифры

$3<1500 && $5=="null" - составной шаблон - коньюнкция (логическое И) двух операций сравнения: третье поле содержит значение, большее 1500, а пятое - строку "null"

NR==5,/^nobody/ - диапазон строк от пятой строки до первой строки, начинающейся с nobody

Вот два примера программ AWK:

$ awk -F: '$3>=1000' /etc/passwd
- однострочная программа, состоящая из шаблона, показывающего все строки в файле /etc/passwd, для которых UID равен 1000 или более

$ awk -F: '{ print $1 }' /etc/passwd
- программа выводит имена всех пользователей из файла /etc/passwd

Опция -F указывает разделитель полей (":" вместо пробела, использующегося по умолчанию).

Ещё примеры программ в одну строку (только тексты программ, без вызывающей команды awk):

NR%10==0
- вывести каждую десятую строку

{ print NR; $0 }
- пронумеровать строки при выводе

{ s = $0"\n"s } END { print s }
- вывести входные строки в обратном порядке

{ printf "%" int(40+lenght($0)/2)" s\n, $0 }
- вывести строки, предварительно выровняв их по центру (по столбцу 40)

NF
- вывести непустые строки.

Последний пример - это тоже законченная программа! NF (число полей) - встроенная переменная AWK, которая обновляется при каждой строке. Если её использовать как шаблон (что мы и сделали), то если NF>0 (TRUE), то действие по умолчанию будет выполнено: строка будет выведена в выходной поток.

Более длинные программы, чем однострочные, лучше помещать в отдельный файл, как в примере ниже.

maxuid.awk (скрипт сканирует файл паролей в поисках наибольшего идентификатора пользователя):
BEGIN { FS=":"; maxuid=0 }
$3>maxuid { maxuid=$3; maxname=$13 }
END { print maxname ": " maxuid }

Запуск в командной строке:
$ awk -f maxuid.awk /etc/passwd

Если добавить в начало скрипта строку #!/usr/bin/awk -f

и установить атрибут исполняемый файлу скрипта, то его можно запускать на выполнение обычным способом:
$ ./maxuid.awk /etc/passwd

Специальные (встроенные) переменные AWK:

ARGC, ARGV - доступ к аргументам командной строки

NF - число полей в текущей строке

NR - номер текущей строки

ENVIRON - переменные окружения в виде ассоциативного массива

FS - разделитель полей (по умолчанию - пробел)

RS - разделитель строк (может применяться регулярное выражение)

OFS - разделитель полей выходного потока (~ оператор print)

IGNORECASE - если параметр установлен, не учитывать регистр символов

FIELDWIDTHS - разделенный запятыми список ширины полей (для потока данных с полями фиксированной ширины)

У AWK есть встроенная библиотека функций, хоть и небольшая. Также при необходимости можно определить и собственные функции. Есть масса функций для работы со строками, есть и математические. На AWK несомненно повлиял язык C. Но при этом применяется динамическая типизация, а не статическая, как в C, и переменные не нужно объявлять до их использования. К тому же, в AWK есть встроенная проверка на соответствие регулярным выражениям.

Некоторые функции для обработки строк:

gsub(r, s) - глобально заменяет r на s в $0

index(s, t) - возвращает позицию первого вхождения t в s или 0, если нет вхождений

lenght(s) - длина строки s (число символов в ней)

match(s, r) - проверяет, содержит ли s подстроку r

split(s, a, fs) - разбивает s в массив a через разделитель fs

substr(s, p, n) - возвращает подстроку строки s длиной n, начиная с позиции p

Некоторые математические функции - sin(), cos(), exp(), sqrt(), rand(). Последняя представляет собой реализацию генератора псевдослучайных чисел в AWK.

Также приведем пример более сложной программы - в плане алгоритма, который она реализует. Пусть она записана в файл seconds.awk с таким содержанием:

{ split($7, hms, ":")
secs = (hms[1]*3600) + (hms[2]*60) + hms[3]
printf "%6d %5d\n", $2, secs }

Эта программа адаптирована к команде ps -ef:
$ ps -ef | awk -f seconds.awk

Вот пример её вывода - в первом столбце - PID, во втором - число секунд работы процесса (фрагмент вывода):

-2

Наконец, алгоритм поиска простых чисел (его уже трудно считать простым) - тоже легко реализуется в AWK в виде небольшого набора инструкций:

-3

Вот первые 20 строк вывода:

-4

В этом алгоритме происходит поиск и вывод на экран простых чисел со значением менее 1000, начиная с трёх. Также показан пример определения функции (isprime).

Таким образом, AWK - полноценный язык программирования: в нём есть переменные, арифметические операции, массивы, циклы, ветвление, функции и всё прочее, что полагается языку программирования. Одно из очевидных преимуществ AWK - то, что он способен выполнять арифметические операции, что не по зубам grep или sed.

_______________________________________________________
Источник: статьи журнала Linux Format (номера LXF138 и LXF177)