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

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

7. Выражения

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

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

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

7.1 Постоянные выражения

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

Простейшим типом выражений являются константы, которые постоянно имеют одно и то же значение. Имеются три типа констант: численные, строковые и константы регулярных выражений.

7.1.1 Численные и строковые константы

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

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

105        1.05e+2             1050e-1

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

Например:

"попугай" представляет цепочку, значение которой есть `попугай'. Цепочки в gawk могут иметь любую длину и могут содержать любые восьмибитовые символы ASCII, включая ASCII NUL (нулевой символьный код). Другие реализации awk могут иметь трудности с некоторыми символьными кодами.

7.1.2 Константы регулярных выражений

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

Такая константа (константа regexp) есть описание регулярного выражения, заключенное в слеши, такое как /^beginning and end$/. Большинство regexp, используемых в awk-программах, есть константы, но операторы соответствия `~' и `!~' могут также определять соответствие вычислимым или "динамическим" regexps (которые являются обычными цепочками или переменными, содержащими некоторую regexp).

Внутреннее представление использует числа с плавающей точкой двойной точности. Если вы не знаете, что это такое, не беспокойтесь об этом.

7.2 Использование констант регулярных выражений

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

Когда такая константа стоит в правой части оператора `~' или `!~' , то она означает только regexp для соответствия.

Константы regexp (такие как /foo/) могут использоваться подобно простым выражениям. Когда константа regexp встречается сама по себе, она имеет то же самое значение как если бы появилась в образце, т.е. `($0 ~ /foo/)' (d.c.) (см. раздел 8.1.3 [Выражения как образцы], стр. 98). Это значит, что два следующих отрезка кодов:

if ($0 ~ /barfly/ ---- $0 ~ /camelot/)
print "found"

и

if (/barfly/ ---- /camelot/)
print "found"

в точности эквивалентны.

Одно довольно странное следствие этого правила состоит в том, что следующее булевское выражение имеет значение 'да', но делает не то, что, вероятно, думает пользователь:


# note that /foo/ is on the left of the ~ if (/foo/ ~ $1) 

print "found foo"

Этот код, "очевидно", проверяет $1 на соответствие regexp /foo/. Но выражение `/foo/ ~ $1' фактически означает `($0 ~ /foo/) ~ $1'. Другими словами, сначала проверяется соответствие входной записи и regexp /foo/. Результат будет ноль или один в зависимости от успеха или неудачи соответствия. Затем этот результат сопоставляется первому полю записи. Поскольку мало вероятно, что кто-то на самом деле хочет выполнить такой тест, gawk выдает предостережение, если видит в программе такую конструкцию.

Другое следствие этого правила состоит в том, что оператор присваивания matches = /foo/ присвоит значение ноль или единица переменной matches, в зависимости от содержания текущей входной записи. Это свойство языка никогда не было хорошо документировано до спецификаций POSIX.

Постоянные регулярные выражения также используются в качестве первого аргумента функций gensub, sub и gsub, и как второй аргумент функций соответствия (см. раздел 12.3 [Встроенные функции для действий с цепочками], стр. 137). Современные реализации awk, включая gawk, допускают в качестве третьего аргумента split константу regexp, в то время как некоторые прежние реализации не допускают (d.c.). Это может привести к недоразумению, при попытках использовать константы regexp в качестве аргумента функций, определенных пользователем (см. главу 13 [Функции пользователя, стр. 153).

Например:


function mysub(pat, repl, str, global) -
if (global)
gsub(pat, repl, str) else
sub(pat, repl, str) return str ""
-
... text = "hi! hi yourself!" 
mysub(/hi/, "howdy", text, 1) ... ""

В этом примере программист хочет передать константу regexp в пользовательскую функцию mysub, которая в свою очередь передаст ее или в sub или в gsub. Однако, на самом деле произойдет следующее: параметр pat будет единицей или нулем в зависимости от того, соответствуют или нет $0 и /hi/. Поскольку маловероятно, что вы на самом деле хотите передать таким путем булевское значение, gawk выдает предостережение, когда видит константу regexp в позиции аргумента в определенной пользователем функции.

7.3 Переменные

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

Переменные представляют средство для запоминания значений в некоторой точке программы для последующего их использования в другой точке программы. Можно манипулировать ими всюду в пределах текста программы и можно также присваивать им значения в командной строке awk.

7.3.1 Использование переменных в программе

Переменные позволяют давать имена значениям для позднейшей ссылки на них. Мы уже видели переменные во многих примерах. Имя переменной должно быть цепочкой букв, цифр и подчеркиваний, но не может начинаться с буквы. Регистр имеет значение в именах переменных; a и A означают разные переменные. Имя переменной само по себе является правильным выражением; оно представляет текущее значение переменной. Новые значения переменные получают посредством операторов присваивания, приращения и уменьшения. См. раздел 7.7 [Присваивающие выражения], стр. 84.

Некоторые переменные имеют специальные встроенные значения, например, разделитель полей FS или количество полей в текущей входной записи NF. См. главу 10 [Встроенные переменные], стр. 115, где содержится их список. Эти встроенные переменные могут использоваться или получать присвоенные новые значения подобно другим переменным, но их значения также используются или автоматически меняются интерпретатором awk. Все имена встроенных переменных записываются буквами верхнего регистра. Переменные в awk могут получать как численные так и строковые значения. По умолчанию переменные инициализируются значениями пустой цепочки, которое в численной форме конвертируется в ноль. Поэтому нет нужды явно инициализировать каждую переменную, как это делается в Си и большинстве других языков.

7.3.2 Присваивание переменным из командной строки

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

Можно установить значение любой переменной awk включением присваивания в аргументы командной строки при запуске awk (см. раздел 14.2 [Другие аргументы командной строки], стр. 165). Такой аргумент имеет вид variable=text. С его помощью можно установить значение переменной или при запуске awk или между входными файлами. Если перед присваиванием стоит параметр `-v', подобно -v variable=text, то переменная устанавливается с самого начала, до выполнения правил BEGIN. Параметр `-v' и его присваивания должны предшествовать всем аргументам с именами файлов и тексту программы. (См. раздел 14.1 [Параметры командной строки], стр. 161, о подробностях употребления параметра `-v'.) В противном случае присваивания происходят в моменты, определенные их позициями среди аргументов входных файлов, после обработки предшествующих аргументов.

Например:

awk '- print $n ""' n=4 inventory-shipped n=2 BBS-list

печатает значение номера поля n для всех входных записей. Перед чтением первого файла командная строка присваивает переменной n значение 4. Это заставляет печатать четвертое поле в строках из файла `inventory-shipped'. После окончания первого файла но перед началом второго файла n получает значение два, так что второе поле печатается в строках из `BBS-list'.

$ awk '- print $n ""' n=4 inventory-shipped n=2 BBS-list
a 15 a 24...
a 555-5553 a 555-3412...

Аргументы командной строки становятся доступными для явного анализа из awk-программы в массиве с именем ARGV (см. раздел 10.3 [Использование ARGC и ARGV], стр. 120). awk обрабатывает значения присваиваний в командной строке и для управляющих последовательностей (d.c.) (см. раздел 4.2 [Управляющие последовательности], стр. 24).

7.4 Преобразования цепочек и чисел

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

Цепочки преобразуются в числа и числа преобразуются в цепочки, если этого требует контекст awk-программы. Например, если значение или foo или bar в выражении `foo + bar' окажется цепочкой, оно превращается в число перед выполнением сложения. Если численные значения встречаются при конкатенации цепочек, они превращаются в цепочки.

 Рассмотрим следующее:

two = 2; three = 3 print (two three) + 4

Это печатает значение (численное) 27. Численные значения переменных two и three превращаются в цепочки и сцепляются вместе, а цепочка-результат опять преобразуется в число 23, к которому добавляется 4.

Если по какой-то причине нужно принудить число превратиться в цепочку, можно сцепить пустую цепочку "" с этим числом. Чтобы превратить цепочку в число нужно прибавить ноль к этой цепочке.

Цепочка превращается в число при интерпретации любого численного префикса как записи числа: "2.5" превращается в 2.5, "1e3" превращается в 1000, а "25fix" получает численное значение 25. Цепочки, которые нельзя интерпретировать как правильные числа, превращаются в 0. Точные правила, по которым числа превращаются в цепочки, определяются встроенной awk-переменной CONVFMT (см. главу 10 [Встроенные переменные], стр. 115).

Числа преобразуются с помощью функции sprintf (см. раздел 12.3 [Встроенные функции для действий с цепочками], стр. 137) и с CONVFMT как указателем формата. CONVFMT по умолчанию имеет значение "%.6g", которое печатает числа по крайней мере с 6 значащими цифрами. Для некоторых приложений требуется изменять ее для получения большей точности. Двойная точность на большинстве современных машин соответствует 16 или 17 десятичным цифрам. Странные результаты могут получиться, если CONVFMT получит значение, которое не помогает sprintf форматировать правильно числа с плавающей точкой. Например, если вы опустите `%' в формате, все числа будут выдаваться одной и той же постоянной цепочкой. Но если число целое, то результат преобразования будет всегда целым, независимо от значения CONVFMT.

 В таком фрагменте кода:

CONVFMT = "%2.2f" a = 12 b = a ""

b получит значение  "12", а не "12.00" (d.c.).

До введения стандарта POSIX, awk предусматривало значение OFMT для преобразования чисел в цепочки. OFMT специфицирует выходной формат для печати чисел оператором print. CONVFMT было введено для отделения семантики преобразования от семантики печати. И CONVFMT и OFMT имеют по умолчанию значение "%.6g". В подавляющем большинстве случаев старые awk-программы не изменят своего поведения. Однако, такое использование OFMT нужно держать в памяти, если вы переносите вашу программу на другую реализацию awk; мы рекомендуем вместо изменения программы переносить сам gawk! См. раздел 6.1 [Оператор print], стр. 61, для полной информации об операторе print.

7.5 Арифметические операторы

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

Язык awk использует привычные арифметические операторы при вычислении значений выражений. Все арифметические операторы имеют нормальные приоритеты и действуют естественным образом. Приведем файл `grades', содержащий список студенческих имен и по три оценки результатов тестов для каждого студента:

Pat 100 97 58 Sandy 84 72 93 Chris 72 92 89

Следующая программа читает файл `grades', и печатает средние значения оценок:

$ awk '- sum = $2 + $3 + $4 ; 
avg = sum / 3 ? print $1, avg ""' grades
a Pat 85
a Sandy 83
a Chris 84.3333

В следующей таблице перечисляются арифметические операции в awk, в порядке уменьшения приоритетов:

- x Отрицание

+ x Одноместный плюс. Превращают выражение в число.

x ^ y или x ** y Возведение в степень: x возводится в степень y.

`2 ^ 3' имеет значение восемь. Цепочка `**' эквивалентна `^'. (Стандарт POSIX содержит только `^' для возведения в степень.)

x * y Умножение. x / y Деление. Поскольку все числа в awk вещественные, то результат не округляется до целого: `3 / 4' имеет значение 0.75.

x % y Остаток. Частное округляется в сторону нуля до целого, умножается на y и результат вычитается из x. Эта операция иногда называется "trunc-mod." Имеет место следующее тождество:

b * int(a / b) + (a % b) == a Один возможный нежелательный эффект такого определения остатка состоит в том, что x % y отрицательно, если x отрицательно. Так, -17 % 8 = -1 В других реализациях awk знак остатка может зависеть от конкретной машины.

x + y Сложение. x - y Вычитание.

Для увеличения переносимости не употребляйте оператор `**'. Одноместные плюс и минус имеют одинаковый приоритет, операторы умножения все имеют одинаковый приоритет, сложение и вычитание имеют одинаковый приоритет.

7.6 Конкатенация цепочек

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

В свое время это казалось хорошей идеей (Brian Kernighan). Имеется только одна операция над цепочками: конкатенация. Нет специального оператора для ее указания. Конкатенация производится записью выражений непосредственно друг за другом.

Например:

$ awk '- print "Field number one: " $1 ""' BBS-list
a Field number one: aardvark
a Field number one: alpo-net...

Без пробела в строковой постоянной после `:', 
строки соединятся вместе.

Например:

$ awk '- print "Field number one:" $1 ""' BBS-list
a Field number one:aardvark
a Field number one:alpo-net...

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


file = "file" name = "name" 

print "something meaningful" ? file name

Необходимо сделать следующее:

print "something meaningful" ? (file name)  

Мы рекомендуем вам использовать скобки вокруг конкатенаций 
почти во всех обычных контекстах (таких как правые части в `=').

7.7 Выражения присваивания

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

Присваивание есть выражение, которое записывает новое значение в переменную. Например, присвоим значение один переменной z:

z = 1 После вычисления этого выражения переменная z получит значение один. Старое значение z будет забыто.

Присваивание может также передавать значение цепочки. Например, присвоим значение "this food is good" переменной message:

thing = "food" 
predicate = "good" 
message = "this " 
thing " is "
predicate

(Это также иллюстрирует конкатенацию строк.) Знак `=' называется оператором присваивания. Это простейшая форма оператора, потому что значение операнда справа сохраняется неизменным.

Большинство операторов (сложение, конкатенация и др.) не производят никаких действий кроме вычисления значения. Если значение игнорируется, оператор можно не использовать. Оператор присваивания не такой; он выдает значение, и даже если вы его игнорируете, все равно переменная изменяется. Мы называем это побочным эффектом.

Левый операнд присваивания не обязан быть только переменной (см. раздел 7.3 [Переменные], стр. 79); он может быть также полем (см. раздел 5.4 [Изменение содержимого поля], стр. 42) или элементом массива (см. главу 11 [Массивы в awk], стр. 123). Все они называются lvalues, что значит, что они могут стоять в левой части оператора присваивания. Правосторонний операнд может быть любым выражением; он выдает новое значение, которое присваивание передает в указанную переменную, поле или элемент массива. (Такие значения называются rvalues).

Важно отметить, что переменные не имеют определенного типа. Ее тип есть тип того значения, которое она хранит в данный момент. В следующем фрагменте программы переменная foo имеет сначала численное значение и потом значение цепочки:

foo = 1 print foo foo = "bar" print foo

Когда второе присваивание дает foo строковое значение, тот факт, что прежнее значение было числом, забывается. Цепочка, которая не начинается с цифры, имеет численное значение нуля. После исполнения следующего кода значением foo будет пять:

foo = "a string" foo = foo + 5

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

Присваивание есть выражение, поэтому оно имеет значение, то же самое, которое присваивается. Так, `z = 1' как выражение имеет значение один. Одно из следствий этого есть то, что можно писать кратные присваивания:

x = y = z = 0 присваивает значение 0 всем трем переменным. Оно делает это, так как значение выражения `z = 0', равное нулю, передается в y и затем значение `y = z = 0', которое есть ноль, передается в x.

Можно использовать присваивание всюду, где появляется выражение. Например, можно написать `x != (y = 1)' чтобы присвоить переменной y единицу, а затем проверить, равен ли х единице. Но такой стиль делает программу трудной для чтения; нигде кроме разовых программ, не следует употреблять таких нагромождений присваиваний.

Кроме `=', имеется несколько других операторов присваивания, которые выполняют арифметические действия с старым значением переменной. Например, оператор `+=' вычисляет новое значение, добавляя правостороннее значение к старому значению переменной. Так, следующее присваивание добавляет пять к значению foo:

foo += 5

Это эквивалентно следующему:

foo = foo + 5

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


# Спасибо  Pat Rankin за этот пример
BEGIN -
foo[rand()] += 5 for (x in foo)
print x, foo[x]
bar[rand()] = bar[rand()] + 5 for (x in bar)
print x, bar[x] ""

Индексы при bar будут гарантированно разными , потому что rand будет возвращать разные значения при каждом вызове. (Массивы и функция rand еще не рассматривались. См. главу 11 [Массивы в awk], стр. 123, и см. раздел 2.2 [Встроенные числовые функции], стр. 136). Этот пример иллюстрирует важный факт в отношении операторов присваивания: только выражение слева вычисляется один раз.

То, в каком порядке вычисляются выражения, левое и правое, зависит от реализации. Рассмотрим пример:

i = 1 a[i += 2] = i + 1

Значение a[3] может быть либо 2 либо 4.

Приведем таблицу арифметических операторов присваивания. В каждом случае правый операнд представляет выражение, значение которого превращается в число.

lvalue += increment
Добавляет increment к значению  lvalue для изменения  lvalue.

lvalue -= decrement
Вычитает  decrement из значения lvalue.

lvalue *= coefficient
Умножает значение lvalue на  coefficient.

lvalue /= divisor
Делит значение  lvalue на  divisor.

lvalue %= modulus
Присваивает lvalue значение ее остатка по modulus.

lvalue ^= power или  lvalue **= power

Возводит lvalue в степень power. (Только оператор `^=' указан в POSIX.) Для максимальной переносимости не употребляйте оператор `**='.

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

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