Язык программирования awk
Код на языке программирования awk состоит не просто из последовательного набора операторов, как в императивном языке, а из набора правил, каждое правило состоит из шаблона и команды.
шаблон {команда}
шаблон {команда}
2
Правила разделяются новой строкой — на одной строке одно правило. awk — интерпретируемый язык, поэтому его можно запускать непосредственно из Bash. В команде, шаблон или правило могут отсутствовать, но не оба одновременно. В следующей программе отсутствует правило, а одна команда выводит каждую строку.
user@debian-server:~/test$ cat text.txt
one
two
three
user@debian-server:~/test$ awk '{print $0}' text.txt
one
two
three
2
3
4
5
6
7
8
Первым параметром программы awk
передается код программы на языке awk, а остальные параметры — файлы для обработки. Программу можно перенести в отдельный файл и запускать awk
с параметром -f
.
user@debian-server:~/test$ cat script.awk
{ print $0 }
user@debian-server:~/test$ awk -f script.awk text.txt
one
two
three
2
3
4
5
6
Интерпретатор awk последовательно читает входные файлы, разбивает их на строки и каждую строку сравнивает с шаблоном в каждом правиле. Если шаблон подходит, то выполняется команда из этого правила. Если шаблон не указан, как в примере выше, то команда выполняется для каждой строки. Следующая программа содержит два одинаковых правила, поэтому на выходе строки дублируются.
user@debian-server:~/test$ cat script.awk
{ print $0 }
{ print $0 }
user@debian-server:~/test$ awk -f script.awk text.txt
one
one
two
two
three
three
2
3
4
5
6
7
8
9
10
Если указан шаблон, то правило срабатывает в случае когда, строка входного соответствует шаблону. Шаблоны указываются в виде регулярных выражений ограниченных символами /
.
user@debian-server:~/test$ cat script.awk
{ print $0 }
/two/ { print $0 }
user@debian-server:~/test$ awk -f script.awk text.txt
one
two
two
three
2
3
4
5
6
7
8
В шаблоне могут быть указаны специальные значения: BEGIN
и END
, эти значения означают начало работы программы и окончание работы программы соответственно.
user@debian-server:~/test$ cat script.awk
BEGIN { print "start reading file" }
END { print "finish reading file" }
{ print $0 }
/two/ { print $0 }
user@debian-server:~/test$ awk -f script.awk text.txt
start reading file
one
two
two
three
finish reading file
2
3
4
5
6
7
8
9
10
11
12
Переменная $0
хранит значение текущей строки. Поэтому команда print $0
выводит текущую строку. Интерпретатор awk, кроме того, что делит входной файл на строки, так же делит строки на поля. По умолчанию, разделителями полей являются пробельные символы. Переменные $1
, $2
, $3
и т. д. возвращают отдельные поля.
user@debian-server:~/test$ cat customers.txt
Tom Smith 2000
Sam Brown 3000
Mark Adams 2500
Paul Ins 4200
John Smith 2800
Tim Cook 2800
user@debian-server:~/test$ nano script.awk
user@debian-server:~/test$ awk '{ print $1 }' customers.txt
Tom
Sam
Mark
Paul
John
Tim
user@debian-server:~/test$ awk '{ print $2 }' customers.txt
Smith
Brown
Adams
Ins
Smith
Cook
user@debian-server:~/test$ awk '{ print $3 }' customers.txt
2000
3000
2500
4200
2800
2800
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Внутри команды можно не просто выводить поля, но и писать более сложный код. В следующем примере используется операция деления и конкатенация строк.
user@debian-server:~/test$ awk '{ print $3 / 10 "quot; }' customers.txt
200$
300$
250$
420$
280$
280$
2
3
4
5
6
7
Можно изменить значение одного поля и вывести всю строк.
user@debian-server:~/test$ awk '{ $1 = "xxx"; $2 = "xxx"; print $0 }' customers.txt
xxx xxx 2000
xxx xxx 3000
xxx xxx 2500
xxx xxx 4200
xxx xxx 2800
xxx xxx 2800
2
3
4
5
6
7
Разделитель полей можно изменить через параметр -F
команды awk
.
user@debian-server:~/test$ awk -F: '{ print $1 }' /etc/passwd
root
daemon
bin
sys
sync
games
...
2
3
4
5
6
7
8
Ещё несколько специальных переменных:
NF
— количество полей в текущей записиNR
— номер строки во всем входном потокеFNR
— номер строки в текущем файле
user@debian-server:~/test$ awk '{ print NR "(" FNR "):" $0 }' text.txt text.txt
1(1):one
2(2):two
3(3):three
4(1):one
5(2):two
6(3):three
2
3
4
5
6
7