КаталогИндекс раздела
НазадОглавлениеВперед


AWK

СОДЕРЖАНИЕ

1. Язык программирования awk
    1.1. Структура программы
    1.2. Лексемы
        1.2.1. Числовые константы
        1.2.2. Текстовые константы
        1.2.3. Ключевые слова
        1.2.4. Идентификаторы
        1.2.5. Знаки операций
        1.2.6. Лексемы для работы с записями и полями
          1.2.6.1. Разделитель записей
          1.2.6.2. Разделитель полей
          1.2.6.3. Записи, состоящие из нескольких строк
          1.2.6.4. Выходные разделители записей и полей
        1.2.7. Комментарии
        1.2.8. Лексемы, используемые для группировки
    1.3. Первичные выражения
        1.3.1. Числовые константы
        1.3.2. Текстовые константы
        1.3.3. Переменные
        1.3.4. Функции
    1.4. Термы
        1.4.1. Бинарные термы
        1.4.2. Унарные термы
        1.4.3. Переменные с приращением
        1.4.4. Термы, заключенные в скобки
    1.5. Выражения
        1.5.1. Конкатенация термов
        1.5.2. Выражения-присваивания

2. Применение awk'а

3. Ввод/вывод
    3.1. Запуск программ на выполнение
    3.2. Ввод: записи и поля
    3.3. Ввод из файла
    3.4. Ввод из командной строки
    3.5. Вывод на печать
    3.6. Вывод в различные файлы
    3.7. Вывод в каналы

4. Шаблоны
    4.1. BEGIN и END
    4.2. Выражения сравнения
    4.3. Регулярные выражения
    4.4. Комбинации шаблонов
    4.5. Шаблоны-диапазоны

5. Действия
    5.1. Переменные, выражения и присваивания
    5.2. Инициализация переменных
    5.3. Переменные-поля
    5.4. Конкатенация цепочек
    5.5. Специальные переменные
    5.6. Типы данных
    5.7. Массивы

6. Специфические возможности
    6.1. Встроенные функции
    6.2. Управляющие конструкции
    6.3. Генерация отчетов
    6.4. Взаимодействие с shell'ом
    6.5. Многомерные массивы


 

1. ЯЗЫК ПРОГРАММИРОВАНИЯ AWK

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

В первой части главы в общих чертах излагается синтаксис awk'а. Затем, начиная с раздела ПРИМЕНЕНИЕ AWK'А, приводится несколько примеров, демонстрирующих использование предоставляемых языком возможностей.

1.1. Структура программы

awk-программа - это последовательность операторов следующего вида:

       шаблон { действие }
       шаблон { действие }
          . . .

awk обрабатывает набор входных файлов. Основной операцией awk'а является просмотр входных строк в порядке их поступления. Каждую строку awk пытается сопоставить с описанным в awk-программе шаблоном. Если сопоставление оказалось успешным, выполняется соответствующее этому шаблону действие. Таким же образом к данной входной строке применяются и все остальные содержащиеся в awk-программе операторы. После того, как проверены все шаблоны, awk переходит к следующей входной строке, причем awk-программа снова выполняется с начала.

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

Например, awk-программа

       /x/     { print }

выводит все входные строки, содержащие символ x.

awk-программа имеет следующую структуру:

Секция BEGIN выполняется перед обработкой входных строк, секция END - после того, как все исходные данные обработаны. Основная секция выполняется для каждой входной строки. Ключевые слова BEGIN и END являются на самом деле шаблонами специального вида, которые распознаются awk'ом.

1.2. Лексемы

Все awk-программы составляются из лексем следующих восьми видов:

  1. Числовые константы.
  2. Текстовые константы.
  3. Ключевые слова.
  4. Идентификаторы.
  5. Знаки операций.
  6. Лексемы для работы с записями и полями.
  7. Комментарии.
  8. Лексемы, используемые для группировки.

1.2.1. Числовые константы

К числовым относятся десятичные константы и константы с плавающей точкой. Десятичная константа - это непустая последовательность цифр, которая может содержать десятичную точку (не более чем одну); например: 12, 12., 1.2, .12. Константа с плавающей точкой состоит из десятичной константы, за которой следуют символы e (либо E), необязательный знак + или - и непустая последовательность цифр; например: 12e3, 1.2e3, 1.2e-3, 1.2E+3. Максимально допустимые размер и точность числовых констант машинно-зависимы.

1.2.2. Текстовые константы

Текстовая константа - это цепочка из нуля или большего числа символов, заключенная в двойные кавычки; например: ",", "a", "ab", "12". Чтобы включить в цепочку сам символ двойной кавычки, перед ним надо поставить знак \: "He said, \"Sit!\"". Чтобы включить в цепочку символ перевода строки, в соответствующем месте надо указать \n. Никаких других управляющих последовательностей не требуется. Текстовые константы могут иметь (практически) любую длину.

1.2.3. Ключевые слова

Следующая таблица содержит используемые в awk'е ключевые слова:

       BEGIN           break           log
       END             close           next
       FILENAME        continue        number
       FS              exit            print
       NF              exp             printf
       NR              for             split
       OFS             getline         sprintf
       ORS             if              sqrt
       OFMT            in              string
       RS              index           substr
                       int             while
                       length

1.2.4. Идентификаторы

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

1.2.5. Знаки операций

awk предоставляет арифметические, логические операции, операции сравнения и присваивания, аналогичные соответствующим операциям языка программирования C, а также операции сопоставления с шаблонами, заданными регулярными выражениями, аналогичные тем, ко- торые используются в egrep(1).

Следующие таблицы содержат описание операций языка awk:

Операции присваивания:

 знак операции   использование   описание 
=  присвоить   
+=  сложить и присвоить   X += Y аналогично X = X+Y
-=  вычесть и присвоить   X -= Y аналогично X = X-Y
*=  умножить и присвоить   X *= Y аналогично X = X*Y
/=  разделить и присвоить   X /= Y аналогично X = X/Y
%=  вычислить остаток и присвоить  X %= Y аналогично X = X%Y
++  префиксное и постфиксное увеличение   ++X и X++ аналогично X = X+1
--  префиксное и постфиксное уменьшение   --X и Y-- аналогично X = X-1

Арифметические операции:

 знак операции   описание 
+  унарный и бинарный плюс 
-  унарный и бинарный минус 
*  произведение 
/  частное 
%  остаток от деления 
(...)  группировка 

Операции сравнения:

 знак операции   описание 
<  меньше 
<=  меньше или равно 
==  равно 
!=  не равно 
>=  больше или равно 
>  больше 

Логические операции:

 знак операции   описание 
&&  и 
||  или 
!  отрицание 

Операции сопоставления:

 знак операции   описание 
~  сопоставляется 
!~  не сопоставляется 

1.2.6. Лексемы для работы с записями и полями

$0 - это специальная переменная, чье значение совпадает со значением текущей входной записи. $1, $2 и т.д. - специальные переменные, чьи значения совпадают со значениями соответственно первого, второго и т.д. полей текущей входной записи. Ключевое слово NF (Number of Fields) обозначает специальную переменную, значение которой равно числу полей в текущей входной записи. Таким образом, значение $NF совпадает со значением последнего поля в текущей записи. Отметим, что нумерация полей в записи начинается с 1, а число полей может изменяться от записи к записи. В действиях, относящихся к шаблонам BEGIN и END, использование перечисленных лексем не имеет смысла, поскольку для этих действий не определена текущая входная запись.

Ключевое слово NR (Number of Records) обозначает специальную переменную, значение которой равно числу входных записей, прочитанных к данному моменту. Первая прочитанная входная запись имеет номер 1.

1.2.6.1. Разделитель записей

Ключевое слово RS (Record Separator) - это специальная переменная, значение которой равно текущему разделителю записей. Первоначально значение RS устанавливается равным символу перевода строки; это означает, что соседние входные записи отделяются одна от другой переводами строк. Значение переменной RS можно заменить на любой символ c, выполнив в действии оператор присваивания RS = "c".

1.2.6.2. Разделитель полей

Ключевое слово FS (Field Separator) - это специальная переменная, значение которой равно текущему разделителю полей. Первоначальное значение FS - пробел; это означает, что поля отделяются произвольными непустыми последовательностями пробелов и табуляций. Значение переменной FS можно заменить на любой одиночный символ c, выполнив в действии оператор присваивания FS = "c" или указав в командной строке необязательный аргумент -Fc. Два значения c имеют особый смысл. Присваивание FS = " " делает разделителями полей пробелы и табуляции, а аргумент командной строки F\t - табуляции.

Если в качестве разделителя полей используется символ, отличный от пробела, считается, что с каждой стороны от разделителя имеется по полю. Например, если разделитель полей равен 1, то запись 1XXX1 состоит из трех полей. Первое и третье - пустые. Если разделитель полей - пробел, поля отделяются друг от друга пробелами и табуляциями и ни одно из NF полей не может быть пустым.

1.2.6.3. Записи, состоящие из нескольких строк

Присваивание RS = " " делает разделителем записей пустую строку, а разделителем полей - непустую последовательность из пробелов, табуляций и, возможно, переводов строк. Такое определение не допускает, чтобы первое или последнее поля в записи были пустыми.

1.2.6.4. Выходные разделители записей и полей

Значение переменной OFS (Output Field Separator) задает выходной разделитель полей; действие print помещает его между полями. После каждой записи print помещает символ, являющийся значением переменной ORS (Output Record Separator). Первоначально ORS устанавливается равным переводу строки, а OFS - пробелу. Данные значения можно заменить любыми другими цепочками при по- мощи присваиваний, например, ORS = "abc" и OFS = "xyz".

1.2.7. Комментарии

Комментарий открывается символом # и заканчивается переводом строки. Пример:

       #    This line is a comment

Комментарий можно добавить в конце любой строки awk-программы.

1.2.8. Лексемы, используемые для группировки

Обычно в awk'е лексемы отделяются друг от друга непустыми последовательностями пробелов, табуляций и переводов строк, а также другими пунктуационными символами, такими как запятые и точ- ки с запятой. В фигурные скобки, {...}, заключаются действия, в наклонные черты, /.../, - шаблоны, заданные регулярными выражениями, в двойные кавычки, "...", - текстовые константы.

1.3. Первичные выражения

В awk'е шаблоны и действия составляются из выражений. Первичные выражения - это основные "строительные блоки" для выражений; к первичным выражениям относятся:

Каждое выражение имеет как числовое, так и текстовое значение; одно из них обычно является предпочтительным. Правила, по которым определяется предпочтительное значение выражения, излагаются ниже.

1.3.1. Числовые константы

Формат числовых констант описан ранее, в разделе Лексемы. Значения таких констант хранятся в виде вещественных чисел. Текстовое значение числовой константы определяется естественным образом. Предпочтительным является числовое значение. Следующая таблица содержит примеры значений числовых констант:

 Числовая константа   Числовое значение   Текстовое значение 
0 0 0
1 1 1
.5 0.5 .5
.5e2 50 50

1.3.2. Текстовые константы

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

 Текстовая константа   Числовое значение   Текстовое значение 
"" 0 пусто
"a" 0 a
"XYZ" 0 XYZ
"o" 0 0
"1" 1 1
".5" 0.5 .5
".5e2" 50 .5e2

1.3.3. Переменные

Переменная - это одна из следующих конструкций:

       идентификатор
       идентификатор [ выражение ]
       $терм

Числовое значение любой неинициализированной переменной равно 0, а текстовое значение - пустой цепочке.

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

Переменная $0 обозначает текущую входную запись. Ее текстовое и числовое значения совпадают со значениями текущей записи. Если текущая входная запись представляет число, то числовое значение $0 равно данному числу, а текстовое значение - соответствующей цепочке символов. Предпочтительным значением $0 является текстовое, если только текущая входная запись не представляет число. $0 нельзя изменить при помощи присваивания.

Переменные $1, $2, ... обозначают первое, второе и т.д. поля. Текстовое и числовое значения $i (1 <= i <= NF) совпадают со значениями i-го поля текущей записи. Так же, как и для $0, если i-е поле представляет число, то числовое значение $i равно данному числу, а текстовое значение - соответствующей цепочке символов. Предпочтительным значением $i является текстовое, если только это поле не представляет число. $i можно изменить при помощи присваивания; соответственно изменяется и значение $0.

В общем случае, $терм обозначает входную запись, если терм имеет числовое значение 0, и i-е поле, если целая часть числового значения терма равна i. Если NF < i <= 100, $i ведет себя так же, как неинициализированная переменная. Манипуляции с $i при i > NF не изменяют значения NF.

1.3.4. Функции

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

       exp (выражение)
       int (выражение)
       log (выражение)
       sqrt (выражение)

Арифметические функции (exp, int, log, sqrt) вычисляют, соответственно, экспоненту, целую часть, натуральный логарифм и квадратный корень числового значения выражения. (выражение) может быть опущено, в таком случае функция применяется к $0. Предпочтительным считается числовое значение арифметической функции.

Операции над цепочками символов:

       getline
       index (выражение1, выражение2)
       length
       length (выражение)
       split (выражение, идентификатор, выражение2)
       split (выражение, идентификатор)
       sprintf (формат, выражение1, выражение2 ...)
       substr (выражение1, выражение2)
       substr (выражение1, выражение2, выражение3)

Выполнение функции getline приводит к тому, что текущая входная запись заменяется на следующую входную запись. Функция возвращает 1, если следующая входная запись существует, и 0, если ее нет. Значение переменной NR обновляется.

Функция index (e1, e2) по текстовым значениям выражений e1 и e2 находит первое вхождение цепочки e2 в e1 и возвращает номер начальной позиции. Если e2 не входит в e1, функция index возвращает 0. Пример:

       index ("abc", "bc") = 2
       index ("abc", "ac") = 0

Функция length без аргументов возвращает число символов в текущей входной записи. Если указан аргумент-выражение, length (e) возвращает число символов в текстовом значении e. Пример:

       length ("abc") = 3
       length (17) = 2

Функция split (e, array, sep) разбивает текстовое значение выражения e на поля, которые помещаются затем в array[1], array[2], ... array[n]; в качестве разделителя полей используется текстовое значение аргумента sep. Результат, возвращаемый функцией, равен числу обнаруженных полей. Если третий аргумент опущен, функция split в качестве разделителя полей использует текущее значение FS. Например, после обращения

       n = split ($0, a)

a[1], a[2], ... a[n] - это та же самая последовательность, что и $1, $2, ... $NF.

Функция sprintf (f, e1, e2, ...) преобразует текстовые значения выражений e1, e2, ... в соответствии с форматом, специфицированным текстовым значением выражения f. Соглашения об управлении форматом такие же, как и для функции printf(3S) в языке программирования C (исключение: не допускается использование символа * для обозначения ширины поля или точности).

Функция substr (string, pos) возвращает окончание цепочки символов string, начиная с позиции pos. Функция substr (string, pos, length) возвращает подцепочку аргумента string, начинающуюся с позиции pos и имеющую длину length. Если длина pos+length больше, чем длина аргумента string, то оба варианта substr эквивалентны. Пример:

       substr ("abc", 2, 1) = "b"
       substr ("abc", 2, 2) = "bc"
       substr ("abc", 2, 3) = "bc"

Значения аргумента pos, меньшие 1, принимаются равными 1. Отрицательное или нулевое значение аргумента length приводит к пустому результату. Предпочтительным для функций sprintf и substr является текстовое значение. Предпочтительное значение остальных функций - числовое.

1.4. Термы

Различные арифметические операции, применяемые к первичным выражениям, образуют более крупные синтаксические элементы, называемые термами. Все арифметические вычисления выполняются над вещественными числами. Терм имеет одну из следующих форм:

       первичное_выражение
       терм биноп терм
       уноп терм
       переменная_с_приращением
       ( терм )

1.4.1. Бинарные термы

В терме вида

       терм биноп терм

биноп может быть одной из пяти арифметических операций +, -, * (умножение), / (деление), % (остаток). Бинарная операция применяется к числовым значениям операндов терм1 и терм2, результат также имеет соответствующее числовое значение, которое является предпочтительным, но его можно интерпретировать и как текстовое (см. пункт Числовые константы). Операции *, / и % имеют более высокий приоритет, чем + и -. Все операции левоассоциативны.

1.4.2. Унарные термы

В терме вида
       уноп терм

уноп может быть унарным + или -. Унарная операция применяется к числовому значению операнда терм, результат имеет соответствующее числовое значение, которое является предпочтительным, однако его можно интерпретировать и как текстовое. Унарные операции + и - имеют более высокий приоритет, чем *, / и %.

1.4.3. Переменные с приращением

Переменная с приращением имеет одну из следующих форм:

       ++пер
       --пер
       пер++
       пер--

Терм ++пер имеет значение пер+1, а по своему действию эквивалентен присваиванию пер = пер+1. Аналогично, терм --пер имеет значение пер-1; он эквивалентен присваиванию пер = пер-1. Далее, терм пер++ имеет значение пер и эффект присваивания пер = пер+1, а терм пер-- имеет значение пер и эффект присваивания пер = пер-1. Предпочтительным значением переменной с приращением является числовое.

1.4.4. Термы, заключенные в скобки

Для группировки термов используются скобки.

1.5. Выражения

Выражения в awk'е имеют одну из следующих форм:

       терм
       терм терм ...
       пер присвоп выражение

1.5.1. Конкатенация термов

В выражении вида терм терм ... текстовые значения термов конкатенируются. Предпочтительным значением результата считается текстовое. Конкатенация термов имеет меньший приоритет, чем бинарные + и -. Например, выражение

       1+2 3+4

имеет текстовое (и числовое) значение 37.

1.5.2. Выражения-присваивания

Выражение-присваивание имеет следующий вид:

       пер присвоп выражение

где присвоп - одна из шести операций присваивания:

       =
       +=
       -=
       *=
       /=
       %=

Предпочтительным считается то же значение пер, что и предпочтительное значение выражения.

В результате присваивания вида

       пер = выражение

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

       пер оп= выражение

эквивалентно присваиванию

       пер = пер оп выражение

где оп - одна из операций +, -, *, /, %. Операции присваивания правоассоциативны и имеют меньший приоритет, чем любая другая операция. Так, выражение a += b *= c-2 эквивалентно последовательности присваиваний

       b = b * (c-2)
       a = a + b

 

2. ПРИМЕНЕНИЕ AWK'А

Задача оставшейся части данной главы - показать синтаксические конструкции awk'а в действии. Разделы соответствуют следующим рассматриваемым вопросам:

 

3. ВВОД/ВЫВОД

3.1. Запуск программ на выполнение

Есть два способа запустить на выполнение awk-программу, состоящую из операторов вида шаблон {действие}:

1. Если программа короткая (одна-две строки), зачастую проще всего указать программу в качестве первого аргумента командной строки:

       awk  'программа'  [имя_файла...]

где программа - это awk-программа, которую надо выполнить, а имя-файла... - необязательный аргумент, определяющий входной файл (файлы). Отметим, что одинарные кавычки, в которые заключен текст программы, нужны для того, чтобы shell воспринял всю эту цепочку (программу) как первый аргумент команды awk. Например, чтобы выполнить над файлом file1 awk-программу

       /x/ { print }

надо передать shell'у команду

       awk '/x/ { print }' file1

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

       awk 'программа' файл1 -

вызывает сначала обработку файла1, а затем - стандартного ввода.

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

       awk -f имя_программы [имя_файла ...]

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

Проиллюстрируем альтернативные способы запуска на обработку awk-программ. Выполнение shell'ом командной строки

       awk 'BEGIN { print "hello, world"; exit }'

приводит к выдаче на стандартный вывод

       hello, world

Эту же awk-программу можно выполнить, поместив ее текст

       BEGIN { print "hello, world"; exit }

в файл awkprog, а затем набрав в shell'е команду

       awk -f awkprog

Результат будет тот же.

3.2. Ввод: записи и поля

awk читает входные записи по одной. Если разделитель записей не переустановлен, запись - это последовательность символов, начиная с той, на которой остановился ввод, и до символа перевода строки либо до конца файла. После того, как цепочка символов считана, она присваивается переменной $0.

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

3.3. Ввод из файла

В качестве примера рассмотрим файл countries. Данный файл состоит из строк, содержащих площадь (в тысячах квадратных миль), население (в миллионах человек) и континент для десяти крупнейших по площади стран мира. (Данные взяты на 1978 год; Россия отнесена к Азии.)

       Russia     8650    262     Asia
       Canada     3852    24      North America
       China      3692    866     Asia
       USA        3615    219     North America
       Brazil     3286    116     South America
       Australia  2968    14      Australia
       India      1269    637     Asia
       Argentina  1072    26      South America
       Sudan      968     19      Africa
       Algeria    920     18      Africa

Широкие промежутки между колонками при первоначальном вводе заданы табуляциями; слова "North" ("South") и "America" отделяется одиночным пробелом. Данный файл будет использоваться в этой главе в качестве исходного во многих awk-программах, он типичен для того рода информации, для обработки которой лучше всего приспособлен awk (смесь слов и чисел, организованных в колонки или поля, разделенные пробелами либо табуляциями).

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

       Russia     8650    262     Asia

После того, как эта запись прочитана awk'ом, она присваивается переменной $0. Если требуется сослаться на всю запись целиком, это делается при помощи $0. Например, действие

       { print $0 }

распечатывает всю запись.

Поля, принадлежащие записи, присваиваются переменным $1, $2, $3 и т.д.; это означает, что в awk-программе для обращения к первому полю текущей записи используется переменная $1, для обращения ко второму полю - переменная $2, для обращения к i-ому полю - переменная $i. Так, в приведенном выше примере (файл countries) для первой записи:

$1 эквивалентно цепочке "Russia"
$2 эквивалентно цепочке "8650"
$3 эквивалентно цепочке "262"
$4 эквивалентно цепочке "Asia"
$5 эквивалентно пустой цепочке
. . .

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

       awk '{ print $4, $1, $3 }' countries

Можно заметить, что она породит не совсем тот вывод, который требуется, поскольку по умолчанию разделителем полей считается не только табуляция, но и пробел. Неудобство состоит в том, что South America и North America содержат пробел. Поэтому правильнее будет использовать команду

       awk -F\t '{ print $4, $1, $3 }' countries

3.4. Ввод из командной строки

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

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

       x=5

Данный оператор awk-программы присваивает переменной x значение 5. Такой вид присваивания можно задать и в командной строке - это еще один способ указать исходные данные для awk-программы. Например, команда

       awk '{ print x }' x=5 -

будет распечатывать значение 5 после прочтения очередной записи. Знак - в конце командной строки необходим для того, чтобы указать, что исходные данные надо брать со стандартного ввода, а не из файла с именем x=5. После ввода команды пользователь должен ввести исходные данные, завершив их сомволом CTRL+D.

Если исходные данные берутся из файла (например, с именем file1), команда должна выглядеть так:

       awk '{ print x }' x=5 file1

Если необходимо изменить разделитель полей или разделитель записей, это также можно сделать в командной строке, как в следующем примере:

       awk -f awkprog RS=":" file1

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

Предусмотрена специальная опция, -Fx; если она указана, значение разделителя полей изменяется с пробела или табуляции на символ x. Например, командная строка

       awk -F: -f awkprog file1

заменяет разделитель полей на символ :. Отметим, что если разделитель полей явно установлен равным табуляции (то есть при помощи опции -F или посредством присваивания переменной FS), пробелы не считаются разделителями полей. Однако обратное не верно: даже если разделитель полей явно установлен равным пробелу, табуляции тем не менее считаются разделителями полей.

3.5. Вывод на печать

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

       { print }

Это одно из простейших действий, выполняемых awk'ом. Оно выдает каждую строку на печать. Полезнее бывает печатать не всю строку, а одно или несколько полей. Например, при использовании файла countries, описанного ранее, командная строка

       awk '{ print $1, $3 }' countries

распечатывает название и население стран:

       Russia 262
       Canada 24
       China 866
       USA 219
       Brazil 116
       Australia 14
       India 637
       Argentina 26
       Sudan 19
       Algeria 18

Точка с запятой в конце списка операторов необязательна. awk допускает как действие

       { print $1 }
так и
       { print $1; }

Эти два действия эквивалентны. Однако, если требуется поместить два awk-оператора в одну строку awk-текста, точка с запятой необходима. Например, число 5 можно напечатать так:

       { x=5; print x }

Скобки для оператора print также необязательны. Действие

       { print $3, $2 }
эквивалентно
       { print ($3, $2) }

Аргументы оператора print, разделенные запятыми, при печати разделяются текущим выходным разделителем полей (обычно пробелом, даже если входные поля разделяются табуляциями). OFS - это еще одна специальная переменная, которую может переустановить программист. (Полностью все такие переменные перечисляются ниже.) print может также выводить на печать цепочки, определенные непосредственно в awk-программах, например

       { print "hello, world " }

Как уже было сказано, awk предоставляет несколько специальных переменных, значениями которых бывает удобно управлять, например, FS и RS. В следующем примере вводятся еще две специальные переменные. NR и NF имеют целочисленные значения, равные соответственно номеру текущей прочитанной записи и числу полей в ней. Так, действие

       { print NR, NF, $0 }

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

       1 4 Russia     8650    262     Asia
       2 5 Canada     3852    24      North America
       3 4 China      3692    866     Asia
       4 5 USA        3615    219     North America
       5 5 Brazil     3286    116     South America
       6 4 Australia  2968    14      Australia
       7 4 India      1269    637     Asia
       8 5 Argentina  1072    26      South America
       9 4 Sudan      968     19      Africa
       10 4 Algeria   920     18      Africa

Программа

       { print NR, $1 }

выводит на печать

       1 Russia
       2 Canada
       3 China
       4 USA
       5 Brazil
       6 Australia
       7 India
       8 Argentina
       9 Sudan
       10 Algeria

Этот пример демонстрирует полезный прием: как приписать к элементам списка их последовательные номера. Оператор print без аргументов печатает текущую входную запись. Чтобы напечатать пустую строку, надо использовать

       { print "" }

awk предоставляет также оператор printf, позволяющий программисту самостоятельно задавать требуемый формат вывода. Для распечатываемых числовых значений print использует по умолчанию формат %.6g. Оператор

       printf "формат", выражение, выражение, ...

преобразует указанные в списке аргументов выражения в соответствии со спецификацией, задаваемой цепочкой формат, и распечатывает их. Оператор printf практически идентичен функции printf(3S) из C-библиотеки. Например, действие

       { printf "%10s %6d %6d\n", $1, $2, $3 }

распечатывает $1 как цепочку из 10 символов (выравненную по правому краю). Второе и третье поля (6-значные числа) образуют аккуратную таблицу, состоящую из двух колонок:

          Russia   8650   262
          Canada   3852   24
           China   3692   866
             USA   3615   219
          Brazil   3286   116
       Australia   2968   14
           India   1269   637
       Argentina   1072   26
           Sudan   968    19
         Algeria   920    18

Оператор printf не порождает автоматически никаких выходных разделителей полей или переводов строк. Программист должен добавить их самостоятельно, как это сделано в данном примере. Можно указывать управляющие символы \n, \t, \b (забой), \r (возврат каретки).

Есть еще одна, третья ситуация, в которой может иметь место выдача на стандартный вывод. Это происходит, когда в awk-операторе не специфицировано действие, а только шаблон. В таком случае распечатывается запись $0 целиком. Например, программа

       /x/

распечатывает все записи, содержащие символ x.

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

       { x="hello"; y="world"
         print x, y
       }

выводит

       hello world

Однако, если запятой нет:

       { x="hello"; y="world"
         print x y
       }

результат будет таким:

       helloworld

Чтобы вывести запятую, можно либо явно указать ее в операторе print:

       { x="hello"; y="world"
         print x"," y
       }

либо в секции BEGIN переопределить OFS:

       BEGIN { OFS=", " }
       { x="hello"; y="world"
         print x, y
       }

В обоих случаях результатом будет:

       hello, world

Отметим, что при печати $0 выходной разделитель полей не используется.

3.6. Вывод в различные файлы

shell позволяет переназначать стандартный вывод в файл. awk также позволяет направить вывод во многие различные файлы, причем это можно сделать внутри awk-программы. Вернемся к входному файлу countries. Допустим, требуется вывести всю информацию о странах из Азии в файл, называемый ASIA, всю информацию о странах из Африки в файл AFRICA, и т.д. Это можно сделать при помощи следующей awk-программы:

       { if ($4 == "Asia") print > "ASIA"
         if ($4 == "Europe") print > "EUROPE"
         if ($4 == "North") print > "NORTH_AMERICA"
         if ($4 == "South") print > "SOUTH_AMERICA"
         if ($4 == "Australia") print > "AUSTRALIA"
         if ($4 == "Africa") print > "AFRICA"
       }

Операторы, управляющие последовательностью вычислений, обсуждаются позднее.

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

       printf > "имя_файла"

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

Подчеркнем, что имя файла заключается в кавычки. При отсутствии кавычек имена файлов трактуются как переменные, которые могут оказаться неинициализированными; в таком случае весь вывод направляется в stdout, если он не переназначен в командной строке.

Если вместо > указать >>, вывод будет добавлен в конец файла, а не заменит его содержимое. Отметим, что существует ограничение на максимальное число файлов, в которые можно таким образом направить вывод. Сейчас оно равно десяти.

3.7. Вывод в каналы

Вывод можно направить не только в обычный файл, но и в канал. Пример:

       {
         if ($2 == "XX") print | "mailx mary"
       }

где mary - имя, под которым пользователь входит в систему. Любая запись со вторым полем, равным XX, отправляется пользователю mary в виде почты. awk ждет, пока не выполнится программа целиком, а затем выполняется команда, соединенная с ней каналом [в данном случае - команда mailx(1)]. Программа

       {
         print $1 | "sort"
       }

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

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

       {
         print ... | "cat -v > /dev/tty"
       }

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

 

4. ШАБЛОНЫ

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

4.1. BEGIN и END

Ключевое слово BEGIN является специальным шаблоном, сопоставляющимся с началом исходных данных перед тем, как считывается первая запись. Ключевое слово END - специальный шаблон, сопоставляющийся с концом исходных данных после того, как обработана последняя строка. Таким образом, BEGIN и END дают возможность перехватить управление до и после обработки исходных данных, чтобы выполнить инициализационные и заключительные действия.

В следующем примере шаблон BEGIN используется для того, чтобы вывести заголовки столбцов. Программа

       BEGIN {print "Country","Area","Population","Continent"}
             { print }

порождает такой текст:

       Country Area Population Continent
       Russia     8650    262     Asia
       Canada     3852    24      North America
       China      3692    866     Asia
       USA        3615    219     North America
       Brazil     3286    116     South America
       Australia  2968    14      Australia
       India      1269    637     Asia
       Argentina  1072    26      South America
       Sudan      968     19      Africa
       Algeria    920     18      Africa

Формат, в котором выводятся заголовки, не очень хорош; в случаях, когда важно качество внешнего представления, обычно используется printf - он больше подходит для такой задачи.

Напомним также, что секцию BEGIN удобно использовать для переустановки специальных переменных, в частности FS и RS. Пример:

       BEGIN { FS= "\t"
       printf "Country\t\t  Area\tPopulation\tContinent\n\n"}
       { printf "%-10s\t%6d\t%6d\t\t% -14s\n", $1, $2, $3, $4 }
       END    { print "The number of records is", NR }

В этой программе FS устанавливается в секции BEGIN равным табуляции, в результате чего все записи в файле countries содержат ровно четыре поля. Рекомендуется указывать BEGIN первым в последовательности шаблонов, а END - последним.

4.2. Выражения сравнения

Произвольные выражения, включающие сравнения цепочек символов или чисел, являются допустимыми шаблонами awk'а. Например, если требуется распечатать информацию только о странах, население которых превышает 100 миллионов, можно использовать шаблон

       $3 > 100

Простая awk-программа, состоящая из одного этого шаблона без всякого действия, распечатает только те записи, значение третьего поля в которых больше 100:

       Russia     8650    262     Asia
       China      3692    866     Asia
       USA        3615    219     North America
       Brazil     3286    116     South America
       India      1269    637     Asia

Чтобы напечатать названия стран, расположенных в Азии, надо набрать программу

       $4 == "Asia"  { print $1 }

Ее результатом будет

       Russia
       China
       India

Проверяемые условия задаются знаками операций <, <=, ==, !=, >=, >. Если в выражении сравнения оба операнда являются числами, выполняется числовое сравнение; в противном случае операнды сравниваются как цепочки символов. Так, шаблон

       $1 >= "S"

отбирает строки, начинающиеся с S, T, U и т.д.; в нашем случае это

       USA        3615    219     North America
       Sudan      968     19      Africa

Если дополнительная информация о типе отсутствует, поля трактуются как цепочки символов, поэтому программа

       $1 == $4

сравнивает первое и четвертое поля как цепочки символов и выводит на печать единственную строку:

       Australia  2968    14      Australia

4.3. Регулярные выражения

Кроме возможностей, проиллюстрированных в предыдущем разделе, в awk'е есть и более мощное средство спецификации шаблонов - регулярные выражения. Простейшим регулярным выражением является буквальное указание требуемой цепочки. Пример (регулярные выражения обрамляются символами /):

       /Asia/

Данное регулярное выражение - законченная awk-программа, печатающая все строки, содержащие какое-либо вхождение цепочки Asia. Если строка содержит более длинное слово, частью которого является цепочка Asia, например Asiatic, она все равно печатается (в файле countries, однако, таких слов нет).

Используемые в awk'е регулярные выражения трактуются так же, как в распознавателе шаблонов egrep(1). Ряд символов имеет специальный смысл.

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

       /^A/

Для печати всех строк, начинающихся с A, B или C, годится регулярное выражение

       /^[ABC]/

Все строки, оканчивающиеся цепочкой ia, распечатываются с помощью шаблона

       /ia$/

В общем случае, символ ^ обозначает начало строки, символ $ - конец строки, а символы, заключенные в квадратные скобки, [], сопоставляются с любым одиночным символом из перечисленных в скобках. Кроме того, awk позволяет использовать круглые скобки для группировки, а символ | - для перечисления альтернатив. Знак + определяет, что предшествовавшее ему выражение должно сопоставляться один или более раз, знак ? обозначает повторение нуль или один раз. Программа

       /x|y/  { print }

печатает все цепочки, содержащие x или y, программа

       /ax+b/  { print }

выводит все цепочки, содержащие символ a, за которым следует один или более символов x, а затем b (например, axb, Paxxxxxxxb, QaxxbR). Программа

       /ax?b/  { print }

печатает все цепочки, содержащие символ a, за которым следует необязательный x, а затем символ b (например, ab, axb, yaxbPPPP, CabD).

Два символа, . и *, имеют тот же смысл, что и в ed(1). Более подробно, . обозначает произвольный символ, а * - нуль или более вхождений предыдущего символа. Так, шаблон

       /a.b/

сопоставляется с любой записью, содержащей символ a, за которым следует произвольный символ, а затем символ b. Другими словами, запись должна включать символы a и b, разделенные произвольным символом. Например, /a.b/ сопоставляются с axb, aPb и xxxxaXbxx, но не с ab, axxb. Шаблон

       /ax*c/

сопоставляется с записью, содержащей символы a и c, разделенные цепочкой из произвольного, возможно нулевого, числа символов x. Например, она сопоставляется с цепочками

       ac
       axc
       pqraxxxxxxxxxxc901

Так же, как и в ed(1), можно отменить специальную интерпретацию метасимволов (например, ^ и *). Для этого перед такими символами ставится знак \. Например, шаблон

       /\/.*\//

сопоставляется с произвольной цепочкой символов, заключенной в "скобки" /.../.

Можно также специфицировать, что некоторое поле (переменная) должно сопоставляться с регулярным выражением (либо не сопоставляться). Это делается при помощи операций ~ и !~. Например, для входного файла countries следующая программа выводит на печать все страны, названия которых заканчиваются цепочкой ia:

       Russia
       Australia
       India
       Algeria

4.4. Комбинации шаблонов

Шаблон может быть составлен из более простых шаблонов, объединенных операциями || (ИЛИ), && (И), ! (ОТРИЦАНИЕ) и скобок. Например, шаблон

       $2 >= 3000 && $3 >= 100

отбирает строки, относящиеся к странам, как площадь, так и население которых достаточно большие:

       Russia     8650    262     Asia
       China      3692    866     Asia
       USA        3615    219     North America
       Brazil     3286    116     South America

Шаблон $4 == "Asia" || $4 == "Africa"

отбирает строки, четвертое поле в которых совпадает либо с Asia, либо с Africa. Альтернативный способ спецификации послед- него шаблона:

       $4 ~ /^(Asia|Africa)$/

Шаблон специфицирует отбор записей, четвертое поле в которых сопоставляется с цепочкой Asia или Africa, причем сопоставление начинается с первого символа и заканчивается последним.

Операции && и || гарантирует, что их операнды обрабатываются слева направо; обработка прекращается, как только установлено истинностное значение выражения.

4.5. Шаблоны-диапазоны

Шаблон в awk'е может также состоять из двух шаблонов, разделенных запятой:

       шаблон1, шаблон2 { действие }

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

       /Canada/,/Brazil/

печатает все строки, расположенные между строкой, содержащей цепочку Canada, и строкой, содержащей цепочку Brazil. Например:

       Canada     3852    24      North America
       China      3692    866     Asia
       USA        3615    219     North America
       Brazil     3286    116     South America

Шаблон

       NR == 2, NR == 5 { ... }

вызывает выполнение действия для входных строк со второй по пятую. Разные типы шаблонов можно смешивать, например:

       /Canada/, $4 == "Africa"

Данный оператор печатает все строки, начиная с той, которая содержит цепочку Canada, и заканчивая той, четвертое поле которой суть Africa.

Примечание

Приведенное выше обсуждение механизма сопоставления с шаблоном относится к разделу шаблонов awk-операторов. Сопоставление с шаблоном может также иметь место в операторах if и while в разделе действий. См. раздел Управляющие конструкции.

 

5. ДЕЙСТВИЯ

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

5.1. Переменные, выражения и присваивания

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

       { print $1 (1000000 * $3) / ($2 * 1000) }

(Напомним, что в этом файле население страны указано в миллионах человек, а площадь - в тысячах квадратных миль.) Результатом является количество людей, приходящееся на одну квадратную милю:

       Russia 30.289
       Canada 6.23053
       China 234.561
       USA 60.5809
       Brazil 35.3013
       Australia 4.71698
       India 501.97
       Argentina 24.2537
       Sudan 19.6281
       Algeria 19.5652

Формат у этой таблицы получился не очень красивым; если использовать вместо print оператор printf:

       { printf "%10s %6.1f\n", $1, (1000000*$3)/($2*1000) }

результат будет таким:

       Russia    30.3
       Canada     6.2
       China    234.6
       USA       60.6
       Brazil    35.3
       Australia  4.7
       India    502.0
       Argentina 24.3
       Sudan     19.6
       Algeria   19.6

Все промежуточные арифметические вычисления выполняются над вещественными числами. Допускаются следующие арифметические операции: +, -, *, /, % (остаток от деления).

Чтобы вычислить суммарное население и число стран из Азии, можно использовать программу

       /Asia/  { pop += $3; ++n }
       END     { print "total population of", n,
                   "Asian countries is", pop }

которая печатает:

       total population of 3 Asian countries is 1765

Операции ++, --, +=, -=, /=, *=, %= используются в awk'е так же, как в языке C. Операция ++, например, увеличивает значение переменной на единицу. Операции ++ и -- (как и в C) могут быть и префиксными, и постфиксными. Все эти операции можно использовать также и в выражениях.

5.2. Инициализация переменных

В предыдущем примере переменные pop и n не были инициализированы; тем не менее, программа работала нормально. Это происходит потому, что (по умолчанию) переменные инициализируются пустой цепочкой, числовое значение которой равно 0. Данное соглашение устраняет необходимость большинства инициализаций переменных в секции BEGIN.

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

       maxpop < $3 {
                     maxpop = $3
                     country = $1
                   }
       END         { print country, maxpop }

Ее результат:

       CHINA 866

5.3. Переменные-поля

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

       { $2 /= 1000; print }

вычислить значение поля, исходя из значений двух других:

       BEGIN   { FS = "\t" }
               { $4 = 1000 * $3 / $2; print }

присвоить полю новое значение - цепочку:

       /USA/   { $1 = "United States" ; print }

Последняя программа заменяет в одной из строк поле USA на United States и печатает новое значение строки:

       United States   3615    219     North America

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

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

Чтобы сконкатенировать цепочки символов, надо написать их одну за другой. Например, программа

       { x = "hello"
         x = x", world"
         print x
       }

печатает

       hello, world

При использовании в качестве входного файла countries, программа

       /^A/            { s = s " " $1 }
       END             { print s }

напечатает

        Australia Argentina Algeria

В конкатенациях можно указывать переменные, текстовые и числовые выражения; числовые значения в данном случае трактуются как текстовые.

5.5. Специальные переменные

Некоторые переменные в awk имеют специальное назначение. В этом разделе приводится полный список таких переменных и описание их использования:

  NR
  Порядковый номер текущей записи.
  NF
  Число полей в текущей записи.
  FS
  Входной разделитель полей, по умолчанию равен пробелу/табуляции.
  RS
  Входной разделитель записей, по умолчанию равен символу перевода строки.
  $i
  i-е поле текущей записи.
  $0
  Текущая входная запись целиком.
  OFS
  Выходной разделитель полей, по умолчанию равен пробелу.
  ORS
  Выходной разделитель записей, по умолчанию равен символу перевода строки.
  OFMT
  Формат для вывода на печать чисел, используется оператором print; по умолчанию равен %.6g.
  FILENAME
  Имя файла, из которого в данный момент производится ввод. Это удобно, поскольку обычно awk-программы имеют вид
         awk -f программа файл1 файл2 файл3 ...

5.6. Типы данных

Переменные (и поля) принимают числовые или текстовые значения в зависимости от контекста. Например, в присваивании

       pop += $3

pop полагается числом, в то время как в присваивании

       country = $1

country - это цепочка символов. В выражении

       maxpop < $3

тип maxpop зависит от данных, которые содержатся в $3, что определяется во время выполнения программы.

В общем случае, каждая переменная (поле) является потенциально цепочкой символов или числом, либо одновременно и тем и другим. Если значение переменной устанавливается присваиванием

        пер = выражение

то ее тип становится равным типу выражения. ("Присваивание" - это также и +=, ++, -= и т.д.) Арифметическое выражение имеет тип число; конкатенация цепочек имеет тип цепочка_символов.

В сравнениях, если оба операнда являются числами, они и сравниваются как числа. В противном случае, операнды, если требуется, преобразуются в цепочки символов и выполняется сравнение этих цепочек.

Следующие трюки позволяют преобразовать тип любого выражения:

       выражение + 0

трактуется как число, а

       выражение ""

- как цепочка символов. (Последнее выражение - это конкатенация с пустой цепочкой.)

5.7. Массивы

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

       x [NR] = $0

присваивает текущую входную строку NR-му элементу массива x. В принципе, если выполнить следующую awk-программу:

               { x [NR] = $0 }
       END     { ... программа ... }

можно в произвольном порядке обрабатывать все исходные данные целиком (хотя, быть может, и довольно медленно). Первая строка этой программы заносит входные записи в массив x.

Программа

       { x [NR] = $1 }

(в случае обработки файла countries) порождает массив со следующим содержимым:

       x [1] = "Russia"
       x [2] = "Canada"
       x [3] = "China"
         . . .

Использование в качестве индексов в массиве нечисловых значений придает awk'у возможности, сходные с возможностями ассоциативной памяти Снобол-таблиц. Например, можно написать программу

       /Asia/    { pop ["Asia"] += $3 }
       /Africa/  { pop ["Africa"] += $3 }
       END       { print "Asia=" pop ["Asia"] ,
                        "Africa=" pop ["Africa"] }

которая порождает результат

       Asia=1765 Africa=37

Отметим, что индексные выражения можно конкатенировать. Кроме того, в качестве индекса при обращении к массиву можно использовать произвольное выражение. Так, в конструкции

       area [$1] = $2

текстовое значение первого поля строки используется как индекс в массиве area.

 

6. СПЕЦИФИЧЕСКИЕ ВОЗМОЖНОСТИ

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

6.1. Встроенные функции

Предоставляемая awk'ом функция length позволяет вычислить длину цепочки символов. Следующая программа печатает длину и содержимое каждой записи:

       { print length, $0 }

В данном случае length эквивалентно length($0), что обозначает длину текущей записи. В общем случае, length (x) возвращает длину аргумента x, трактуемого как цепочка символов.

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

       length ($1) > max { max = length ($1); name = $1 }
       END     { print name }

Функция split в форме

       split (s, array)

присваивает поля цепочки s последовательным элементам массива array. Например, обращение

       split ("Now is the time", w)
присваивает значение Now элементу w[1], is - w[2], the - w[3], time - w[4]. Все другие элементы массива w[] устанавливаются равными пустой цепочке. Можно указать, что роль разделителя полей должен играть не пробел, а другой символ. В таком случае используется иная форма функции split, с тремя аргументами:
       n = split (s, array, sep)

Данный вызов разбивает цепочку s на поля и помещает их в array[1], ..., array[n]. Результат, возвращаемый split, равен числу обнаруженных полей. Если аргумент sep указан, задаваемая им цепочка используется в качестве разделителя полей; в противном случае используется FS. Это бывает удобно, если требуется в середине awk-программы переопределить для одной записи разделитель полей.

Кроме того, awk предоставляет математические функции

       sqrt
       log
       exp
       int

Это функции вычисления квадратного корня, натурального логарифма, экспоненты и целой части числа. Последняя функция возвращает максимальное целочисленное значение, не превосходящее значения аргумента. Перечисленные функции заимствуются из математичекой библиотеки языка C (awk-функции int соответствует функция floor библиотеки libm), поэтому в случае ошибки они возвращают такие же результаты, что и их аналоги из libm (см. Справочник программиста).

Функция substr в форме

       substr (s, m, n)

возвращает подцепочку цепочки s, начинающуюся с позиции m и содержащую не более n символов. Если третий аргумент (в данном случае - n) отсутствует, выделяется подцепочка до конца s. Например, программа

       { $1 = substr ($1, 1, 3); print }

позволяет сократить названия стран в файле countries:

       Rus     8650    262     Asia
       Can     3852    24      North America
       Chi     3692    866     Asia
       USA     3615    219     North America
       Bra     3286    116     South America
       Aus     2968    14      Australia
       Ind     1269    637     Asia
       Arg     1072    26      South America
       Sud     968     19      Africa
       Alg     920     18      Africa

Если s - число, substr использует его текстовое представление:

       substr (123456789, 3, 4) = 3456

Функция

       index (s1, s2)

возвращает номер начальной позиции первого вхождения цепочки s2 в цепочку s1, либо нуль, если цепочка s2 не входит в цепочку s1.

Функция sprintf форматирует выражения так же, как это делает оператор printf, однако не отправляет результат на стандартный вывод, а присваивает его некоторой переменной. Например, оператор

       x = sprintf ("%10s %6d", $1, $2)

присваивает переменной x цепочку символов, полученную форматным преобразованием значений $1 и $2, после чего x можно использовать в последующих вычислениях.

Функция getline немедленно читает следующую входную запись. Значения полей, переменных $0 и NR переустанавливаются, однако управление остается в том же самом месте awk-программы. getline возвращает 0, если обнаружен конец файла, и 1, если считана обычная запись.

6.2. Управляющие конструкции

awk позволяет использовать в действиях следующие управляющие конструкции:

        if-else

        while

        for

и составной оператор, такой же, как в языке C.

Оператор if имеет следующий вид:

       if ( условие ) оператор1 else оператор2

Условие вычисляется; если оно истинно, выполняется оператор1; в противном случае выполняется оператор2. Часть else является необязательной. Несколько операторов, заключенных в фигурные скобки, трактуются как единый оператор. В примере, приведенном в разделе Инициализация переменных, вычисление максимума населения можно перенести из шаблона в действие, если воспользоваться оператором if:

       { if (maxpop < $3) {
           maxpop = $3
           country = $1
         }
       }
       END     { print country, maxpop }

Оператор while имеет вид:

       while ( условие ) оператор

Условие вычисляется; если оно истинно, выполняется оператор. Условие вычисляется снова, и если оно истинно, опять выполняется оператор. Цикл повторяется до тех пор, пока условие истинно. Например, следующая программа распечатывает все входные поля, по одному на каждой строке:

       { i = 1
         while (i <= NF) {
           print $i
           ++i
         }
       }

Другой пример - алгоритм Евклида нахождения наибольшего общего делителя $1 и $2:

       { printf "the greatest common divisor of "
           $1 "and ", $2, "is"
         while ($1 != $2) {
           if ($1 > $2) $1 -= $2
           else $2 -= $1
         }
         printf $1 "\n"
       }

Оператор for, аналогичный соответствующей конструкции языка C, имеет вид

       for ( выражение1 ; условие ; выражение2 ) оператор

Так,

       { for (i = 1 ; i <= NF; i++)
           print $i
       }

- это еще одна awk-программа, распечатывающая все входные поля, по одному на каждой строке.

Имеется альтернативная форма оператора for, удобная для доступа к элементам ассоциативного массива в awk:

       for ( i in массив ) оператор

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

               { x [NR] = $0 }
       END     { for (i in x) print i, x [i] }

Более содержательным является следующий пример - индексы-цепочки используются для вычисления суммарного населения стран по континентам:

       BEGIN   { FS="\t" }
               { population [$4] += $3 }
       END     { for (i in population)
                   print i, population [i]
               }

В данной программе тело цикла for выполняется для i, равного по очереди различным названиям континентов, до тех пор, пока все возможные значения i не будут исчерпаны (то есть пока все цепочки-названия не будут использованы). Отметим, однако, что порядок вычислений не определен. Например, такая программа может напечатать:

       Africa 37
       South America 142
       Asia 1765
       North America 243
       Australia 14

Отметим, что условие в операторах if, while и for может включать:

Оператор break (если он встречается внутри циклов while или for) приводит к немедленному выходу из цикла.

Оператор continue (если он встречается внутри циклов while или for) приводит к началу следующей итерации цикла.

Встретившийся в awk-программе оператор next заставляет awk немедленно перейти к следующей записи и возобновить просмотр шаблонов с начала программы. (Отметим различие между getline и next: getline не ведет к переходу к началу awk-программы.)

Если оператор exit встречается в секции BEGIN awk-программы, программа прекращает свое выполнение и выполняется секция END (если она есть).

Если оператор exit встречается в основной секции awk-программы, прекращается выполнение основной секции. Последующие записи не читаются, выполняется секция END.

Оператор exit в секции END приводит к завершению выполнения программы.

6.3. Генерация отчетов

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

       Smith   draw  3
       Brown   eqn   1
       Jones   nroff 4
       Smith   nroff 1
       Jones   spell 5
       Brown   spell 9
       Smith   draw  6

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

               { use [$1 "  " $2] += $3 }
       END     { for (np in use)
                   print np "\t" use [np] | "sort +0 +2nr"
               }

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

       Brown   eqn   1
       Brown   spell 9
       Jones   nroff 4
       Jones   spell 5
       Smith   draw  9
       Smith   nroff 1

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

       { if ($1 != prev) {
           print $1 ":"
           prev = $1
         }
         print "\t" $2 "\t" $3
       }

Переменная prev используется для того, чтобы убедиться, что каждое уникальное значение $1 печатается ровно один раз. Команда

       awk -f list1 prog.usage | awk -f format1

выдаст такой результат:

       Brown:
               eqn     1
               spell   9
       Jones:
               nroff   4
               spell   5
       Smith:
               draw    9
               nroff   1

Часто оказывается удобным объединить несколько разных awk-программ с другими командами shell'а, такими как sort(1), что и было сделано в программе list1.

6.4. Взаимодействие с shell'ом

Обычно awk-программа либо помещается в файл, либо указывается в командной строке (при этом ее текст заключается в одинарные кавычки):

       awk '{ print $1 }' ...

Использование одинарных кавычек позволяет избежать интерпретации shell'ом текста awk-программы. Это необходимо, потому что многие специальные символы awk'а совпадают со специальными сим- волами shell'а (например, $ или ").

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

       field n

выполняла бы команду

       awk '{ print $n }'

Как передать значение n в awk-программу?

Это можно сделать несколькими способами. Во-первых, можно поместить в файл field строку

       awk '{ print $'$1' }'

В данном случае существенно отсутствие пробелов: эта запись воспринимается как единый аргумент, несмотря на то, что указаны две пары кавычек. $1 находится вне кавычек, доступен shell'у и, следовательно, должным образом подставится в текст программы при выполнении shell-процедуры field.

Еще один способ решения задачи основывается на том, что shell интерпретирует $-параметры, которые содержатся в цепочках, заключенных в двойные кавычки:

       awk "{ print \$ $1 }"

Небольшая хитрость состоит в экранировании первого символа $ с помощью \; $1, как и в предыдущем случае, заменяется при обращении к field на требуемое число.

6.5. Многомерные массивы

Формируя специального вида индексы, можно смоделировать многомерные массивы. Например, в действии

       for (i = 1; i <= 10; i++)
         for (j = 1; j <= 10; j++)
           mult [i ',' j] = ...

создается массив, индексы в котором имеют вид i,j, то есть 1,1, 1,2 и так далее; тем самым моделируется двумерный массив.


НазадОглавлениеВперед
КаталогИндекс раздела