Руководство пользователя для GNU Awk

Arnold D. Robbins
перевод Балуева А. Н.

8. Образцы и действия

Оглавление
В начало страницы

Как мы уже видели, каждый awk-оператор состоит из образца с соответствующим действием. В этой главе описывается, как строить образцы и действия.

8.1 Элементы образцов

Образцы в awk управляют выполнением правил: правило выполняется, когда его образец соответствует текущей входной записи. В этом разделе говорится все о том, как писать образцы.

8.1.1 Типы образцов

В начало страницы
Приведем обзор типов образцов, используемых в awk.

/regular expression/

Регулярное выражение может служить образцом. Он соответствует, когда текст входной записи отвечает регулярному выражению. (См. главу 4 [Регулярные выражения], стр. 23.)

expression

Отдельное выражение. Оно соответствует, если его значение не ноль (в случае числа) или не пусто (в случае цепочки) (См. раздел 8.1.3 [Выражения как образцы], стр. 98.)

pat1, pat2

Два образца, разделенные запятой, указывают диапазон записей. Диапазон включает начальную запись, соответствующую pat1, и конечную запись, соответствующую pat2. (См. раздел 8.1.4 [Указание диапазонов записей образцами], стр. 99.)

BEGIN END

Специальные образцы для начальных и заключительных действий awk-программы. (См. раздел 8.1.5 [Специальные образцы BEGIN и END], стр. 100.)

empty

Пустой образец соответствует каждой входной записи. (См. раздел 8.1.6 [Пустой образец], стр. 102.)

8.1.2 Регулярные выражения как образцы

В начало страницы

Мы употребляли регулярные выражения как образцы с первых примеров. Этот тип образцов есть просто константа regexp в части правила, отведенной для образца. Их смысл заключается в `$0 ~ /pattern/'. Образец соответствует, если входная запись соответствует regexp.

 Например:

/foo--bar--baz/ - buzzwords++ "" END - print buzzwords, "buzzwords seen" ""

8.1.3 Выражения как образцы

В начало страницы

Каждое выражение awk может быть образцом awk. Тогда образец соответствует, если значение выражения не ноль (в случае числа) или не пусто (в случае цепочки). Выражение пере вычисляется каждый раз, когда правило проверяется для новой входной записи. Если в выражении используются поля, такие как $1, значение зависит непосредственно от текста новой входной записи; в противном случае оно зависит только от того, что произошло до сих пор при выполнении awk-программы, но все еще может быть полезным.

Самым обычным типом выражений, используемых в качестве образца, служат выражения сравнения, содержащие операторы сравнения, описанные в разделе 7.10 [Типы переменных и выражения сравнения], стр. 88.

Соответствия и несоответствия с regexp также представляют обычно употребляемые выражения. Левый операнд в операторах `~' и `!~' есть цепочка. Правый операнд есть или постоянное регулярное выражение, заключенное в слеши (/regexp/) или любое выражение, строковое значения которого используется как динамическое регулярное выражение (см. раздел 4.7 [Использование динамических Regexps], стр. 35).

В следующем примере печатается второе поле каждой входной записи , у которой первое поле есть в точности `foo'.

$ awk '$1 == "foo" - print $2 ""' BBS-list

(Здесь нет выхода, поскольку в BBS нет экранов с именем "foo".) В отличие от соответствия следующего регулярного м выражения, которое выбирает всякую запись с первым полем, содержащим `foo':

$ awk '$1 ~ /foo/ - print $2 ""' BBS-list

a 555-1234 a 555-6699 a 555-6480 a 555-2127

Булевские выражения также обычно используются в качестве образцов. Соответствует ли образец входной записи зависит от того, соответствуют ли друг другу их подвыражения. Например, такая команда печатает все записи из `BBS-list', которые содержат вместе `2400' и `foo':

$ awk '/2400/ && /foo/' BBS-list
a fooey 555-1234 2400/1200/300 B

А следующая команда печатает все записи из `BBS-list', которые содержат или `2400' или `foo', или и то и другое:


$ awk '/2400/ ---- /foo/' BBS-list
a alpo-net 555-3412 2400/1200/300 A
a bites 555-1675 2400/1200/300 A
a fooey 555-1234 2400/1200/300 B
a foot 555-6699 1200/300 B
a macfoo 555-6480 1200/300 A
a sdace 555-3430 2400/1200/300 A
a sabafoo 555-2127 1200/300 C

Следующая команда печатает все записи из  `BBS-list', 
которые не содержат цепочки `foo':

$ awk '! /foo/' BBS-list
a aardvark 555-5553 1200/300 B
a alpo-net 555-3412 2400/1200/300 A
a barfly 555-7685 1200/300 A
a bites 555-1675 2400/1200/300 A
a camelot 555-0542 300 C
a core 555-2912 1200/300 C
a sdace 555-3430 2400/1200/300 A

Подвыражения булевского оператора в образце могут быть постоянными регулярными выражениями, сравнениями или любыми другими awk-выражениями. Образцы для диапазонов не являются выражениями и не могут появиться внутри булевских образцов. Точно также специальные образцы BEGIN и END, которые никогда не сопоставляются ни одной входной записи, не есть выражения и не могут появиться внутри булевских выражений. Константа regexp как образец также представляет специальный случай выражения-образца. /foo/ как выражение имеет значение один, если `foo' появится в текущей входной записи; так, как образец, /foo/ соответствует любой записи, содержащей `foo'.

8.1.4 Указание диапазонов записей с помощью образцов

В начало страницы

Образец для диапазона образуется двумя образцами, разделенными запятой, вида `begpat, endpat'. Он определяет диапазон последовательных входных записей. Первый образец, begpat, определяет начало диапазона, а второй, endpat, определяет его конец.

Например,

awk '$1 == "on", $1 == "off"' печатает каждую запись внутри пар

`on'/`off', включая граничные случаи.

Образец диапазона начинает работу сравнением begpat с каждой входной записью; если запись соответствует begpat, образец диапазона переходит в состояние "on", а запись считается соответствующей диапазону. Пока остается состояние "on", образец автоматически соответствует каждой прочтенной записи. Также каждая читаемая запись проверяется на соответствие с endpat; если оно имеет место, образец диапазона переходит опять в состояние "off" для следующих записей, и затем опять начинается проверка на begpat для каждой записи.

Запись, которая включает образец диапазона, и та, которая его выключает, обе соответствуют образцу диапазона. Если вы не хотите учитывать эти записи, вы можете написать операторы if в правилах действия, чтобы отделить их от записей, представляющих интерес для вас. Для образца возможно быть включенным и выключенным одной и той же записью, если она удовлетворяет обоим условиям. Тогда действия выполняются только для этой записи.

Например, предположим что вы имеете текст между двумя одинаковыми маркерами (скажем, между символами `%'), который вы хотите игнорировать. Вы можете скомбинировать образец диапазона, который описывает выделенный текст, с оператором next (он еще не рассматривался, см. раздел 9.7 [Оператор next], стр. 111), который побуждает awk опустить всякую дальнейшую обработку текущей записи и начать с начала обработку следующей записи.

Такая программа может как будто выглядеть так:

/^%$/,/^%$/ - next ""
- print ""

Но эта программа терпит неудачу, потому что образец диапазона и включается и выключается первой строкой с `%' на ней. Для выполнения задачи нужно написать программу так, используя флажок:

/^%$/ - skip = ! skip; 

next "" skip == 1 - next "" # пропустить строки с `skip' set

Заметим что в образце диапазона `,' имеет низший приоритет (вычисляется последней). Так, например, следующая программа пытается скомбинировать образец диапазона с другим, более простым тестом:

echo Yes -- awk '/1/,/2/ ---- /Yes/'

Автор этой программы предполагал, что она будет работать как

`(/1/,/2/) ---- /Yes/'. Однако, awk интерпретирует ее как
 `/1/, (/2/ ---- /Yes/)'. Это не может быть изменено или работать дальше;
 
образцы диапазонов не комбинируются с другими образцами.

8.1.5 Специальные образцы BEGIN и END

В начало страницы

BEGIN и END служат специальными образцами. Они не используются для сравнения с входными записями. Они обеспечивают стартовые или заключительные действия в сценарии awk.

8.1.5.1 Стартовые и заключительные действия

В начало страницы

Правило BEGIN выполняется один раз, до чтения первой входной записи. Правило END выполняется один раз, после прочтения всего ввода.

Например:


$ awk ' ? BEGIN - print "Analysis of ""foo""" "" ? /foo/ - ++n "" ? END&
 - print """foo"" appears " n " times." ""' BBS-list
a Analysis of "foo"
a "foo" appears 4 times.

Эта программа определяет количество записей в входном файле `BBS-list', которые содержат цепочки `foo'. Правило BEGIN печатает заголовок отчета. Нет нужды использовать правило BEGIN для инициализации счетчика n нулем, так как awk делает это автоматически (см. раздел 7.3 [Переменные], стр. 79). Второе правило увеличивает переменную n каждый раз, когда читается запись, содержащая образец `foo'. Правило END печатает значение n в конце работы.

Специальные образцы BEGIN и END не могут использоваться в диапазонах или в булевских операторах (вернее, они не могут использоваться ни с какими операторами).

Одна awk-программа может иметь много BEGIN и/или END правил. Они выполняются в порядке их расположения, все правила BEGIN при старте и все правила END при окончании. BEGIN и END правила могут перемежаться с другими правилами. Эта возможность была добавлена в awk-версии 1987 и входит в стандарт POSIX. Оригинальная (1978) версия awk требует помещать правило BEGIN в начале программы, а правило END в конце, и допускает только по одному каждого вида. Этого больше не требуется, но это рекомендуется для лучшей организации и читаемости программы.

Кратные правила BEGIN и END полезны при написании библиотечных функций, поскольку каждый библиотечный файл может иметь свое собственное правило BEGIN и/или END для собственной инициализации и/или завершения. Заметим, что порядок, в котором библиотечные функции называются в командной строке, определяет порядок, в котором выполняются их правила BEGIN и END. Поэтому нужно писать эти правила в библиотечном файле так, чтобы порядок их исполнения был безразличен. См. раздел 14.1 [Параметры командной строки], стр. 161, о подробностях использования библиотечных функций. См. главу 15 [Библиотека функций awk], стр. 169, содержащей сведения о многих библиотечных функциях.

Если awk-программа имеет только правило BEGIN и никаких других правил, то она заканчивается после выполнения правила BEGIN. (Начальная версия продолжала чтение до конца файла, игнорируя входные данные.) Однако если существует правило END, ввод читается даже если в программе нет других правил. Это необходимо в случае, когда правило END проверяет переменные FNR и NR (d.c.).

Правила BEGIN и END должны иметь действия; для них не предусмотрены действия по умолчанию, поскольку при их исполнении нет никаких текущих записей.

8.1.5.2 Ввод/вывод из правил BEGIN и END

В начало страницы

Имеется несколько вопросов (иногда тонких), связанных с I/O в правилах BEGIN или END. Первый из них относится к значению $0 в правиле BEGIN. Так как правила BEGIN выполняются до чтения всякого ввода, то в это время еще нет входной записи и никаких полей. Ссылки на $0 и поля дают пустые цепочки или нули, в зависимости от контекста. Один способ придать $0 реальное значение состоит в выполнении команды getline без переменной (см. раздел 5.8 [Явный ввод с помощью getline], стр. 53). Другой путь состоит в присваивании ей некоторого значения.

Другой вопрос подобен первому, но относится к противоположному концу. Какое значение имеют внутри правила END переменные $0 и NF? Традиционно считалось, что $0 и NF неопределенны внутри правила END. В стандарте POSIX указано, что NF указывает количество полей в последней входной записи. По-видимому по недосмотру в стандарте не указано, что $0 также сохраняет значение последней записи. gawk оставляет это значения для использования в правиле END. Но знайте, что Unix awk и, возможно, другие реализации этого не делают.

Третий вопрос вытекает из двух первых. Каков смысл `print' внутри правил BEGIN или END? Обычно его смысл `print $0'. Если $0 есть пустая цепочка, то печатается пустая строка. Издавна программисты awk употребляют `print' в правилах BEGIN и END в смысле `print ""', уповая на то, что $0 пуста. Хотя вы об этом можете не заботиться в правиле BEGIN, во всяком случае в gawk, это не приведет к добру в END. И это плохой стиль. Если вы хотите получить пустую строку в выводе, это всегда можно сделать явным образом.

8.1.6 Пустой образец

В начало страницы

Пустой (т.e. не существующий) образец рассматривается как соответствующий любой входной записи.

Например, программа:

awk '- print $1 ""' BBS-list

печатает первое поле каждой записи.

8.2 Обзор действий

В начало страницы

Каждая awk-программа или сценарий состоит из последовательности правил и определений функций, перемежающихся между собой. (Функции будут описаны позже). См. главу 13 [Функции, определенные пользователем], стр., 153.) Правило содержит образец и действие, причем то или другое (но не оба вместе) могут быть опущены. Цель действия --- сообщить awk, что нужно делать, если обнаружено соответствие образцу.

 В общих чертах awk-программа выглядит так:

[pattern] [- action ""] [pattern] [- action ""] ...

function name(args) - ... "" ...

Действие состоит из одного или более операторов awk, заключенных в фигурные скобки (`-' и `""'). Каждый оператор указывает определенное действие. Операторы разделяются символами newlines или точками с запятой. Фигурные скобки вокруг действий должны указываться даже если действие содержит только один оператор или вообще не содержит операторов. Однако, если действие опускается целиком, опускайте и фигурные скобки. Опущенное действие эквивалентно `- print $0 ""'.

/foo/ - "" # соответствие foo, действий нет - пустое действие
/foo/ # соответствие foo, печать записи - опущенное действие

Перечислим типы операторов, определенных в awk:

Выражения, которые могут вызывать функции или присваивать значения переменным (см. главу 7 [Выражения], стр. 77). Выполнение операторов этого типа сводится к вычислению значения выражения. Это действенно, когда выражение имеет побочные эффекты (см. раздел 7.7 [Присваивающие выражения], стр. 84).

Управляющие выражения, которые управляют порядком исполнения awk-программ. Язык awk имеет конструкции, подобные конструкциям в языке Cи (if, for, while, do) и несколько специальных (см. главу 9 [Управляющие операторы в действиях], стр. 105).

Составные операторы, которые состоят из одного или более операторов, заключенных в фигурные скобки. Составные операторы используются для того, чтобы объединить несколько операторов в один в теле if, while, do или for операторов.

Операторы ввода, использующие команду getline (см. раздел 5.8 [Явный ввод с помощью getline], стр. 53), оператор next (см. раздел 9.7 [оператор next], стр. 111), и оператор nextfile (см. раздел 9.8 [Оператор nextfile], стр. 112). Операторы вывода print и printf. См. главу 6 [Печать результатов], стр. 61. Операторы удаления для удаления элементов массивов. См. раздел 11.6 [Операторы удаления], стр. 128. Следующая глава описывает в деталях операторы управления.

В начало страницы

<<< Оглавление Страницы: 8  9 >>>