jim tty4 sep 29 11:27
$
Поля обозначаются как
$1
, $2
, …, $NF
, где NF
— переменная, значение которой установлено равным числу полей. В нашем случае NF=5
для обеих строк. (Учтите разницу между NF
, числом полей и $NF
— последним полем строки. В отличие от интерпретатора в программе awk
только номера полей начинаются с $
; переменные не имеют такого префикса.) Например, следующая команда выдаст поле "размер файла" из результата выполнения команды du -а
$ du -a | awk '{print $2}'
а для печати имен пользователей, работающих в системе, и времени входа нужно задать:
$ who awk '{print $1, $5}'
you 11:53
jim 11:27 $
Для печати имени и времени входа в систему, упорядоченных по времени, зададим:
$ who awk '{print $5, $1}' | sort
11:27 jim
11:53 you
$
Это альтернативные решения примеров, приведенных выше в данной главе, в которых использовалась команда
sed
. Хотя с программой awk
проще работать в подобных случаях, она обычно выполняется медленнее как в начальной фазе, так и при большом входном потоке.Обычно предполагается, что поля разделяются произвольным числом пробелов и символов табуляций, но можно определить в качестве разделителя любой одиночный символ. Один из способов состоит в задании в командной строке флага
-F
(здесь прописная буква). Например, поля в файле паролей /etc/passwd
разделяются двоеточиями:$ sed 3q /etc/passwd
root:3D.fHR5KoB.3s:0:1:S.User:/:
ken:y.68wdl.ijayz:6:1:K.Thompson:/usr/ken:
dmr:z4u3dJWbg7wCk:7:1:D.M.Ritchie:/usr/dmr:
$
Для печати имен пользователей, образующих первое поле, можно задать:
$ sed 3q /etc/passwd | awk -F : '{print $1}'
root
ken
dmr
Обработка пробелов и символов табуляции здесь особая. По умолчанию и пробелы, и символы табуляции служат разделителями, а разделители в начале строки отбрасываются. Однако если в качестве разделителя определен не пробел, то разделители в начале строки учитываются при определении полей. В частности, если используется символ табуляции, то пробелы не являются символами разделителями, пробелы в начале строки вводят в поле, и каждый символ табуляции определяет поле.
ПечатьВ программе
awk
, помимо числа входных полей, доступна и другая интересная информация. Встроенная переменная NR
хранит номер текущей входной "записи", т.е. строки. Поэтому для вставки номера строки перед строкой входного потока достаточно задать:$ awk '{print NR, $0}'
Поле
$0
обозначает всю входную строку без изменений. В операторе print
фрагменты, отделяемые запятой, печатаются через символы разделения полей выходного потока, которые по умолчанию служат пробелами.Формат печати оператора
print
обычно является приемлемым
. При несоответствующем формате используйте оператор printf
, обеспечивающий полный контроль над выходным потоком. Например, для печати номеров строк в поле размером в четыре цифры можно задать такую команду:$ awk '{printf "%4d %s\n", NR, $0}'
Выражение
%4
задает десятичное целое число (NR
) в поле размером в четыре цифры, %S
— строка символов ($0
), \n
— символ перевода строки, который нужен потому, что оператор printf
не выдает автоматически пробелы или символы перевода строк. Оператор printf
сходен с аналогичной Си функцией (см. справочное руководство по printf(3)
).Мы могли бы определить программу
ind
(рассматривавшуюся в начале главы) следующим образом:$ awk '{printf "\t%s\n", $0}' $*
Здесь выдается символ табуляции (
\t
) и входная строка.ШаблоныПредположим, что вы хотите найти в файле
/etc/passwd
пользователей, не имеющих пароля. Зашифрованный пароль находится во втором поле, поэтому программа состоит из одного шаблона:$ awk -F: '$2 == ""' /etc/passwd
Шаблон проверяет, является ли второе поле пустой строкой (операция
==
— это проверка на равенство).Такой шаблон можно задать различными способами:
$2==""
Второе поле пусто$2~/^$/
Второе поле соответствует пустой строке$2!~/./
Второе поле не содержит ни одного символаlength($2) == 0
Длина второго поля равна нулюСимвол
~
обозначает соответствие регулярному выражению, а символ !
— отсутствие соответствия. Само регулярное выражение заключено в символы дробной черты.Встроенная функция
length
программы awk вычисляет длину строки символов. Шаблону может предшествовать символ ! для отрицания его, например,!($2=="")
Операция
!
подобна такой же операции в языке Си, но в редакторе sed
эта операция следует за шаблоном.Наиболее типичное использование шаблонов в программе
awk
сводится к задачам простой проверки данных. Большинство из них немногим сложнее, чем поиск строк, не удовлетворяющих какому-то критерию; если нет выходного потока, то считается, что данные удовлетворяют соответствующему критерию (по принципу "отсутствие новостей — хорошая новость"). Например, в следующем шаблоне проверяется с помощью операции %
, вычисляющей остаток от деления, четно или не четно число полей в каждой входной строке:$ NF % 2 != 0 # напечатать, если нечетное число полей
Другой шаблон выдает исключительно длинные строки, используя встроенную функцию
length
:length ($0) >72 # напечатать, если слишком длинная строка
В программе
awk
используется то же соглашение о комментарии, что и в интерпретаторе: символ #
отмечает начало комментария.Можно сделать выходной поток более информативным, снабдив его предупреждающим сообщением и частью слишком длинной строки, используя для этого встроенную функцию
substr
:length($0) > 72 {print "Строка", NR, "длинная" : substr($0, 1, 60)}
Функция
substr(s, m, n)
выделяет подстроку из строки s, начинающуюся с символа с номером m
и длиной в n
символов. (Символы в строке нумеруются с 1.) Если n
отсутствует, то берется подстрока от m
до конца строки. Эту функцию можно использовать для выделения полей с фиксированным положением, например выделить время в часах и минутах из результата выполнения команды date
:$ date
Thu Sep 29 12:17:01 EDT 1983
$ date | awk '{print substr($4, 1, 5) }'
12:17
$
Упражнение 4.7Сколько различных программ
awk
вы можете составить для переписи входного потока в выходной, как это делает команда cat
? Какая из них самая короткая?Шаблоны BEGIN
и END
Программа
awk
имеет два специальных шаблона BEGIN
и END
. Действия, соответствующие BEGIN
, выполняются прежде, чем читается первая входная строка; можно использовать этот шаблон для инициации переменных, печати заголовков или для установки символа разделителя полей, присваивая его переменной