Пт 17 ноября 2017
By Oleg Mazko
In Admin .
tags: regex
«AWK» — интерпретируемый Тьюринг-полный язык программирования, предназначенный для обработки текста и создания отчетов. Необычное название языка происходит от первых букв фамилий авторов — Alfred V. A ho , Peter J. W einberger и Brian W. K ernighan .
«AWK» рассматривает входной поток как список записей. Программа на «AWK» представляет из себя набор правил вида «шаблон — действие», где шаблон является регулярным выражением , а действие — последовательностью команд или функций. Если шаблон не указан, то действие выполняется для любой записи. Если не указано действие, то запись выводится в выходной поток. Каждая запись поочерёдно сравнивается со всеми шаблонами, и каждый раз когда она соответствует шаблону, выполняется указанное действие. Каждая запись в свою очередь делится на поля. Упрощенно, если говорить об обычном тексте и не учитывать возможности настройки «AWK», то запись — это строка , а поле — это слово в строке.
Если сравнивать редактор SED и язык «AWK», то в первую очередь стоит вспомнить, что оба они являются Тьюринг-полными. Это даёт основание утверждать, что в теории любая задача может быть успешно решена как на «SED» так и на «AWK», вопрос только в том, сколько времени и нервов это займёт. «AWK» выводит обработку текстовых данных на более высокий уровень. Благодаря «AWK» в нашем распоряжении оказывается полноценный язык программирования, а не ограниченный набор команд, отдаваемых редактору. Математические операции, поддержка переменных и ассоциативных массивов, управляющие конструкции if-then
, циклы и т.д. всё это поддерживается в «AWK» из коробки.
ОБРАЗЕЦ
GIST | Простой пример «AWK» — обычная читалка текстовых файлов с выводом на экран. При оформлении этого и последующих примеров будем придерживаться следующего порядка: сверху параметры командной строки, внизу стандартные потоки слева ввода stdin
и справа вывода stdout
:
'{print}'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
Из полезных опций командной строки, которые можно указать при запуске «AWK», стоит выделить возможность указать символ-разделитель для полей -F
и возможность объявить переменную и задать её значение по умолчанию -v var=value
. Как видно из примера, команда print
без дополнительных параметров печатает все содержимое текущей записи.
ТАБЛИЦЫ
GIST | «AWK» отлично справляется с файлами, структурированными предсказуемым образом. К примеру, эта команда особенно сильна в анализе и обработке табличных данных. По умолчанию для разделения полей «AWK» использует пробельные символы и символы табуляции, а для разделения записей используется символ новой строки. Каждому полю автоматического назначается своя переменная согласно его порядковому номеру следования в записи:
'{print $1}'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
George
John
Thomas
GIST | Иногда в качестве разделителей полей удобно использовать не пробелы или символы табуляции, а какой-то особый шаблон. Выше мы упоминали ключ -F
, теперь пример, где разделитель полей задаётся с помощью регулярного выражения :
-F '[-,]+' '{print $1 " ("$3-$2")"}'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
George Washington (8)
John Adams (4)
Thomas Jefferson (8)
Для более тонкой настройки в «AWK» предусмотрен свой собственный комплект встроенных переменных. Одна из таких переменных с именем RS
позволяет задать особый разделитель записей. Подробнее о встроенных переменных чуть позже.
РЕГУЛЯРНЫЕ ВЫРАЖЕНИЯ И БЛОКИ
GIST | «AWK» позволяет использовать регулярные выражения для избирательного выполнения отдельных блоков программы в зависимости от того, совпадает или нет регулярное выражение с текущей записью:
'/on/ { print }'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
George Washington, 1789-1797
Thomas Jefferson, 1801-1809
Если не указано действие, то по умолчанию запись выводится в выходной поток и потому в данном случае действие { print }
можно было бы вообще не указывать.
ЛОГИЧЕСКИЕ ВЫРАЖЕНИЯ И БЛОКИ
GIST | Есть и другие способов избирательно выполнять блок программы. Мы можем поместить перед блоком программы любое булево выражение для управления исполнением этого блока и «AWK» будет выполнять блок программы только если предыдущее булево выражение истинно:
'$1 == "John"'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
John Adams, 1797-1801
GIST | «AWK» предлагает полный набор операторов сравнения, в том числе обычные ==
, <
, >
, <=
, >=
и !=
. Для сложных выражений стоит помнить об условном операторе if
:
'{
if ($1 == "John" && $2 ~ /Ada/) {
print
}
}'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
John Adams, 1797-1801
Операторы ~
и !~
означают совпадает или не совпадает значение слева с регулярным выражением справа.
НЕОБЯЗАТЕЛЬНЫЕ БЛОКИ BEGIN И END
GIST | Часто встречаются ситуации, когда требуется выполнить код инициализации перед тем, как «AWK» начнет обрабатывать текст. Для таких случаев «AWK» дает возможность определять необязательный блок BEGIN
. Это отличное место для инициализации встроенных или глобальных переменных, вывода заголовка и т.д.:
'BEGIN {print "Hello", "World !"}'
Есть еще один необязательный блок END
. «AWK» выполняет этот блок после того, как все записи были обработаны. Обычно блок END
используется для выполнения заключительных вычислений или вывода итогов, которые должны появиться в конце выходного потока.
ВСТРОЕННЫЕ ПЕРЕМЕННЫЕ
«AWK» — динамически типизированный язык: все примитивы хранятся как строки, хотя могут обрабатываться как числа в зависимости от контекста их использования (например, в арифметических выражениях). Основная структура данных языка — ассоциативный массив с индексами-строками.
Мы уже рассматривали позиционные переменные — $1
, $2
, $3
, которые позволяют извлекать значения полей из записей. На самом деле их довольно много и вот список некоторых из наиболее часто используемых:
GIST | ARGC
— число аргументов командной строки. В данном примере вся логика ограничивается блоком BEGIN
, до непосредственной обработки текста дело даже не доходит:
'BEGIN {print "Arguments =", ARGC}' One Two Three Four
GIST | ARGV
— массив аргументов командной строки. В данном примере массив перебирается с помощью цикла. Циклы используются во множестве языков программировании, поддерживает их и «AWK»:
'BEGIN {
for (i = 0; i < ARGC - 1; ++i) {
printf "ARGV[%d] = %s\n", i, ARGV[i]
}
}' one two three four
any text
ARGV[0] = mawk
ARGV[1] = one
ARGV[2] = two
ARGV[3] = three
GIST | NF
позволяет обращаться к последнему полю данных в записи, не зная его точной позиции. «AWK» автоматически устанавливает значение этой переменной равным числу полей в текущей записи:
-F "[, ]+" '{print $NF ":", $1, $(NF-1)}'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
1789-1797: George Washington
1797-1801: John Adams
1801-1809: Thomas Jefferson
GIST | NR
хранит номер текущей записи, начиная с единицы:
'NR < 3'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
George Washington, 1789-1797
John Adams, 1797-1801
Многие встроенные переменные, например ARGC
, устанавливаются автоматически и обычно не предназначены для записи в них, т.е. используются только для чтения.
ПОЛЬЗОВАТЕЛЬСКИЕ ПЕРЕМЕННЫЕ
GIST | Как и любые другие языки программирования, «AWK» позволяет программисту объявлять переменные. Имена переменных могут включать в себя буквы, цифры, символы подчёркивания, но не могут начинаться с цифры. Объявить переменную, присвоить ей значение можно как из командной строки -v var=value
так и непосредственно в коде:
-v msg="Hello World !"
'BEGIN {
num1="21"
num2=21
print num1*2, num2*2, msg + 1
}
{ print msg }'
a
b
42 42 1
Hello World !
Hello World !
Допускается производить математические операции над строками. Если в строковой переменной не содержится правильного числа, то при вычислении математического выражения «AWK» будет обращаться с этой переменной как с числовым нулем.
АССОЦИАТИВНЫЕ МАССИВЫ
GIST | Из структур данных в «AWK» поддерживаются только ассоциативные массивы. В ассоциативном массиве хранятся пары вида ключ => значение . Предполагается, что ассоциативный массив не может хранить две пары с одинаковыми ключами:
'BEGIN {
fruits["mango"] = "yellow";
fruits["orange"] = "orange"
print fruits["orange"] "\n" fruits["mango"]
}'
anything here
orange
yellow
GIST | Для удаления элемента из массива по его ключу предусмотрено ключевое слово delete
:
'BEGIN {
fruits[1] = "yellow";
fruits[2] = "orange";
delete fruits["2"];
for (fruit in fruits) {
print fruits[fruit]
}
}'
Для перебора всех элементов массива предусмотрена конструкция for...in
.
ФУНКЦИИ
GIST | «AWK» имеет набор встроенных функций для выполнения некоторых в известном смысле стандартных операций. К встроенным относятся функции для работы со строками, временем и арифметикой. В следующем примере время конечно же будет отличаться от указанного:
'BEGIN {print strftime("%T",systime()), cos(0), toupper("hello")}'
some text
20:14:27 1 HELLO
GIST | Сложные программы часто могут быть упрощены определением ваших собственных функций. Пользовательские функции могут вызываться точно так же как и встроенные, но определять их вы должны сами:
'# Script execution starts here
BEGIN {
main(10, 20)
}
# Returns minimum number
function find_min(num1, num2){
if (num1 < num2)
return num1
return num2
}
# Returns maximum number
function find_max(num1, num2){
if (num1 > num2)
return num1
return num2
}
# Main function
function main(num1, num2){
# Find minimum number
result = find_min(10, 20)
print "Minimum =", result
# Find maximum number
result = find_max(10, 20)
print "Maximum =", result
}'
anything here
Minimum = 10
Maximum = 20
«AWK» читает всю программу перед началом ее выполнения, так что необязательно помещать определения функций перед вызовом, они могут располагаться где угодно между правилами программы в любой последовательности. Правильное имя функции подобно правильному имени переменной: последовательность букв, цифр и подчеркиваний, начинающаяся не с цифры.
There are comments .