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

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

9. Управляющие операторы в действиях

Оглавление

Управляющие операторы, такие как if, while, и т.д., управляют порядком выполнения частей awk-программ. Большинство управляющих операторов в awk похожи на подобные операторы в Си. Все они начинаются со специального ключевого слова, такого как if или while, выделяющего их среди простых выражений. Многие управляющие операторы содержат другие операторы; например, оператор if содержит другой оператор, который должен или не должен исполняться. Содержащийся оператор называется телом. Если нужно включить в тело более одного оператора, их нужно сгруппировать в один составной оператор с помощью фигурных скобок, разделяя их символами newlines или точками с запятой.

9.1 Оператор if-else

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

Оператор if-else есть оператор принятия решения в awk. Он выглядит так: if (condition) then-тело [else else-тело]. condition есть выражение, которое определяет действие остальной части оператора. Если condition имеет значение true, то выполняется then-тело; в противном случае выполняется else-тело. Часть else может отсутствовать. condition считается имеющим значение false, если оно равно нулю или пустой цепочке, и true в остальных случаях.

Вот пример:

if (x % 2 == 0)
print "x is even" else
print "x is odd"

В этом примере, если выражение `x % 2 == 0' есть true (т.е. значение x без остатка делится на два), выполняется первый оператор print, в противном случае выполняется второй print.

Если else стоит в той же самой строке, что и then-тело и then-тело не есть составной оператор (т.e. не заключено в фигурные скобки), то точка с запятой должна отделять then-тело от else. Чтобы проиллюстрировать это, перепишем предыдущий пример:

if (x % 2 == 0) print "x is even"; else
print "x is odd"

Если вы забудете написать `;', awk не сможет интерпретировать оператор и укажет синтаксическую ошибку. Мы не должны были на самом деле писать в таком виде этот пример, потому что читатель-человек может не заметить else, если оно не стоит в начале своей строки.

9.2 Оператор while

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

В программировании циклом называется часть программы, которая может исполняться последовательно два или более раз. Оператор while представляет простейший циклический оператор в awk. Он повторяет выполнение некоторого оператора, пока условие сохраняет значение true.

Выглядит он так:

while (condition)
body

Здесь body означает оператор, который мы называем телом цикла, а condition есть выражение, которое управляет длительностью повторения тела.

Первое, что делает оператор while, есть вычисление condition. Если condition имеет значение true, выполняется оператор body. После этого опять проверяется condition и если оно все еще имеет значение true, оператор body выполняется снова. Этот процесс продолжается, пока condition имеет значение true. Если condition изначально имеет значение false, то тело цикла не выполняется ни одного раза и awk продолжает работу с оператора, следующего за циклом.

Следующий пример печатает первые три поля 
каждой записи, по одному в строке.

awk '- i = 1
while (i != 3) -
print $i i++ "" ""' inventory-shipped

Здесь тело цикла есть составной оператор, заключенный в скобки, содержащий два оператора. Цикл работает следующим образом: сначала переменной i присваивается значение 1. Затем while проверяет, верно ли что i меньше или равно трем. Это верно при i равном единице, так что печатается i-е поле. Затем `i++' увеличивает значение i и цикл повторяется. Он прекращает работу, когда i достигнет четырех.

Как вы видите, переход к новой строке (newline) не требуется между условием и телом; но новая строка делает программу понятнее, если тело не является составным оператором или очень просто. Новая строка после открывающей скобки также не требуется, но программа без нее становится более трудно читаемой.

9.3 Оператор do-while

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

Цикл do есть вариант цикла while. Цикл do выполняет тело один раз, а затем повторяет его пока condition имеет значение true.

Он выглядит так:

do body while (condition)

Даже если condition есть false с самого начала, body исполняется по крайней мере один раз (и только раз, если исполнение тела не даст условию значение true). В противоположность этому, такой оператор while:

while (condition)
body

не выполняет тело ни одного раза, если условие с самого начала имеет значение false.

Вот пример оператора do:

awk '- i = 1
do -
print $0 i++ "" while (i != 10) ""'

Эта программа печатает каждую входную запись десять раз. Этот пример не очень впечатляет, поскольку в этом случае обычный while будет действовать так же хорошо. Но это соответствует реальному опыту; операторы do необходимы достаточно редко.

9.4 Оператор for

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

Оператор for позволяет удобно считать повторения цикла. Общая форма оператора for выглядит так:

for (initialization; condition; increment)
body

Части инициализация, условие и приращение представляют произвольные выражения awk, а тело есть произвольный awk-оператор. Оператор for начинается с вычисления initialization. Затем, пока condition есть true, повторяются выполнения body а затем increment. Обычно initialization устанавливает переменную на значение ноль или один, increment добавляет к ней единицу, а condition сравнивает ее с желаемым числом повторений.

Вот пример оператора for:

awk '- for (i = 1; i != 3; i++)
print $i ""' inventory-shipped

Он печатает первые три поля каждой входной записи, по одному полю в строке. Можно поместить более одной переменной в части initialization или использовать кратные присваивания типа `x = y = 0', которые, правда, дают одинаковые начальные значения всем переменным. (Но вы можете также инициализировать дополнительные переменные, написав их присваивания как отдельные операторы перед циклом for.) То же самое относится и к части increment; чтобы изменять дополнительные переменные, можно писать отдельные операторы в конце цикла. Сложное выражение Си, использующее оператор comma этого языка, было бы полезно в этом контексте, но оно не включено в awk.

Чаще всего increment есть выражение приращения как в предыдущем примере. Но это не обязательно; оно может быть любым выражением. Например, такой оператор печатает все степени двух между единицей и 100:

for (i = 1; i != 100; i *= 2)
print i

Любые из трех выражений в скобках после for могут быть опущены, если не нужны. Так, `for (; x ? 0;)' эквивалентно `while (x ? 0)'. Если опущено условие, оно трактуется как true, что приводит к бесконечному циклу (т.е. к циклу, который никогда не кончается).

Во многих случаях цикл  for есть сокращение цикла while:

initialization while (condition) -
body increment ""


Единственное исключение имеет место когда оператор продолжения (continue) (см. раздел 9.6 [Оператор continue], стр. 110) использован внутри цикла; превращая оператор for в оператор while таким путем, мы меняем действие оператора continue внутри цикла.

Имеется и другая версия цикла for для итераций 
по всем индексам массива:

for (i in array)
do something with array[i]

См. раздел 11.5 [Сканирование всех элементов массива], стр. 127, содержащий больше информации об этой версии цикла for.

Язык awk имеет оператор for в дополнение к операторам while, потому что часто такой оператор цикла и проще писать и легче понимать. Учет количества итераций очень часто используется в циклах. Может быть, что об этом счете легче думать как о части повторения (looping), чем о чем-то внутри цикла.

Следующий раздел содержит более сложные примеры циклов.

9.5 Оператор break

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

Оператор прерывания break выводит из циклов for, while или do, в которых он находится. Следующий пример находит наименьший делитель любого целого и отмечает простые числа:

awk '# находит наименьший делитель для num
- num = $1
for (div = 2; div*div != num; div++)
if (num % div == 0)
break if (num % div == 0)
printf "Smallest divisor of %d is %d"n", num, div else
printf "%d is prime"n", num ""'

Если в первом операторе if остаток равен нулю, awk немедленно прерывает цикл for. Это означает переход к оператору, непосредственно следующему за циклом и продолжение обработки. (Это совершенно не похоже на оператор exit, который прекращает выполнение программы awk. См. раздел 9.9 [Оператор exit], стр. 112.)

Вот другой пример программы, эквивалентной предыдущей. Он иллюстрирует, как condition цикла for или while можно заменить на оператор break внутри оператора if:

awk '# находит наименьший делитель для num

- num = $1
for (div = 2; ; div++) -
if (num % div == 0) -
printf "Smallest divisor of %d is %d"n", num, div break "" if (div*div ? num) -
printf "%d is prime"n", num break "" "" ""'

Выше уже говорилось, что оператор break не имеет смысла, если расположен вне тела цикла. Однако, хотя это нигде не документировано, по традиции реализации awk рассматривали оператор break вне цикла как оператор next (см. раздел 9.7 [оператор next], стр. 111). Последние версии Unix awk больше не разрешают такое употребление. gawk поддержит такое употребление, если в командной строке будет указан параметр `--traditional' (см. раздел 14.1 [Параметры командной строки], стр. 161).

В противном случае оно будет рассматриваться как синтаксическая ошибка, поскольку стандарт POSIX указывает, что break может использоваться только внутри тела цикла (d.c.).

9.6 Оператор continue

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

Оператор continue, подобно break, употребляется только внутри for, while и do циклов. Он означает игнорирование остатка тела цикла, побуждая немедленно перейти к началу цикла. В противоположность break, который заставляет совсем покинуть цикл. Оператор continue в цикле for предписывает awk пропустить остаток тела и продолжить работу с выражения increment в составе цикла. Это иллюстрирует следующая программа:

awk 'BEGIN -
for (x = 0; x != 20; x++) -
if (x == 5)
continue printf "%d ", x "" print "" ""'

Эта программа печатает все числа от 0 до 20, за исключением числа 5, для которого печать пропускается. Поскольку приращение `x++' не пропускается, x не останавливается на пяти. В отличие от этого примера с циклом for, по-другому ведет себя цикл с while в следующем примере:

awk 'BEGIN -
x = 0 while (x != 20) -
if (x == 5)
continue printf "%d ", x x++ "" print "" ""'

Эта программа зацикливается, когда х достигает 5.

Как сказано выше, оператор continue не имеет смысла, если стоит вне тела цикла. Однако, хотя это нигде не документировано, исторически реализации awk трактовали этот оператор вне цикла как оператор next (см. раздел 9.7 [Оператор next], стр. 111). Последние версии Unix awk не допускают больше это. gawk допускает такое употребление continue только при указании в командной строке параметра `--traditional' (см. раздел 14.1 [Параметры командной строки], стр. 161). В противном случае это трактуется как ошибка, поскольку стандарт POSIX требует, чтобы оператор continue использовался только внутри тела цикла d.c.).

9.7 Оператор next

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

Оператор next понуждает awk немедленно прекратить обработку текущей записи и перейти к следующей записи. Это значит, что последующие правила не применяются к текущей записи. Также не выполняются оставшиеся действия текущего правила .

Не совсем так он действует в случае функции getline (см. раздел 5.8 [Явный ввод по getline], стр. 53). Он тоже побуждает awk прочесть немедленно следующую запись, но никак не меняет ход управления. То есть остаток текущих действий переносится на следующую запись.

На самом верхнем уровне выполнение awk-программы есть цикл, который читает входную запись и проверяет ее на соответствие образцу каждого правила. Если представлять себе этот цикл как оператор for, тело которого содержит правила, то оператор next аналогичен оператору continue: он пропускает конец тела этого неявного цикла и вычисляет increment (т.е, читает следующую запись).

Например, если ваша awk-программа работает только на записях с четырьмя полями и вы не хотите ее прекращения при несоответствующем вводе, вы можете использовать такое правило около начала программы:

NF != 4 -
err = sprintf("%s:%d: skipped: NF != 4"n", FILENAME, FNR) print err ?&
 "/dev/stderr" next ""

так что следующие правила не увидят плохую запись. Сообщение об ошибке перенаправляется по стандартному потоку выдачи ошибок, как и следует для сообщений об ошибках. См. раздел 6.7 [Специальные имена файлов в gawk], стр. 72.

Соответственно стандарту POSIX, поведение не определено, если оператор next использован в правиле BEGIN или END. gawk трактует это как синтаксическую ошибку. Хотя POSIX допускает это, некоторые другие реализации не терпят оператор next внутри тел функций (см. главу 13 [Функции, определенные пользователем], стр. 153). Также как и операторы next в других позициях, next внутри тела функции читает следующую запись и начинает ее обработку с первого правила программы. Если оператор next приводит к концу ввода, то выполняется код из любого правила END. См. раздел 8.1.5 [Специальные образцы BEGIN и END], стр. 100.

ВНИМАНИЕ: некоторые реализации awk генерируют динамическую ошибку, если вы используете оператор next внутри пользовательской функции (см. главу 13 [Функции, определенные пользователем], стр. 153). gawk не имеет таких проблем.

9.8 Оператор nextfile

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

gawk предусматривает оператор nextfile, подобный оператору next. Однако, вместо прекращения обработки текущей записи, оператор nextfile побуждает gawk прекратить обработку текущего файла данных.

При выполнении оператора nextfile переменная FILENAME получает значение имени следующего файла с данными, указанного в командной строк, FNR устанавливается на единицу, ARGIND увеличивается и начинается обработка с первого правила программы. См. главу 10 [Встроенные переменные], стр. 115.

Если оператор nextfile обнаруживает конец ввода, то выполняется код из любого правила END. См. раздел 8.1.5 [Специальные образцы BEGIN и END, стр. 100.

Оператор nextfile есть расширение gawk; оно (в настоящее время) отсутствует в других реализациях awk. См. раздел 15.2 [Реализация nextfile как функция], стр. 170, потому что можно в рамках функции пользователя моделировать действия nextfile. Он полезен, если нужно обрабатывать много файлов с данными.

ВИМАНИЕ: версии gawk до 3.0 использовали два слова (`next file') в названии оператора nextfile. В версии 3.0 это стало одним словом, поскольку `file' имел два смысла. После слова next он был ключевым словом, а в других позициях обычным идентификатором. Старое использование все еще поддерживается, однако gawk генерирует предостережение и в следующих версиях вообще запретит его.

9.9 Оператор exit

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

Оператор exit приказывает awk немедленно прекратить выполнение текущего правила и перестать обрабатывать ввод. Оставшийся ввод игнорируется.

Он выглядит так:

exit [return code]

Если exit выполняется из правила BEGIN, то программа немедленно останавливается и ни одна запись не читается. Однако, если присутствует правило END, оно выполняется (см. раздел 8.1.5 [Специальные образцы BEGIN и END], стр. 100).

Если exit использован как часть правила END, он предписывает немедленную остановку программы.

Если exit не входит ни в BEGIN ни в END, он останавливает выполнение всех дальнейших правил для текущей записи, останавливает ввод дальнейших записей и выполняет правило END, если оно имеется.

Если вы не хотите выполнения END в этом случае, можно установить любую переменную на не ноль перед оператором exit и проверить эту переменную в правиле END. См. раздел 15.3 [Установления], стр. 172, с примером этого. Если exit снабжен аргументом, его значение используется как код статуса exit в процессе выполнения awk. Если аргумента нет, exit выдает нулевой статус (успех). В случае, когда аргументом снабжен первый оператор exit, а сработал другой exit без аргумента, то используется код статуса первого exit (d.c.).

Например, вы обнаружили ошибочную ситуацию, с которой вы не знаете, что делать. Обычно в таком случае программа сообщает это выходом с ненулевым кодом статуса. В awk-программе это можно сделать оператором exit с ненулевым аргументом.

Вот пример:

BEGIN -
if (("date" -- getline date.now) != 0) -
print "Can't get system date" ? "/dev/stderr" exit 1 "" print "current&
 date is", date.now close("date") ""
В начало страницы

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