(Занятие 10) awk - язык сканирования и обработки текста
Операционная система UNIX (руководство пользователя)
vsit, Friday 04 August 2006 - 09:48:04

[newpage=Основные сведения ]
10. awk - язык сканирования и обработки текста

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

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

10.1. Основные сведения об awk

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

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

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

Структура awk:


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


Пример.


    $ -"address" {print $2, $3}


В этом примере приведена типичная программа awk, состоящая из одного выражения шаблон-действие. Программа печатает второе и третье поля каждой вводной строки, в которой первое поле является "address".

Любой шаблон или действие в выражении шаблон-действие может быть опущен. Отсутствие части "действие" означает печать соответствующих шаблону строк. Отсутствие шаблона означает, что действие выполняется над каждой строкой.

Вы можете запустить awk двумя способами. Первый: вы можете задать командную строку:

awk 'шаблон-действие' [список вводных файлов] чтобы выполнить шаблон-действие в перечисленных вводных файлах. Например,


    awk '{print $2, $3}' файл1 файл2


Обратите внимание, что выражение шаблон-действие заключено в одиночные кавычки. Это защищает символы типа $ от интерпретации командным языком shell и позволяет также программе обрабатывать более одной строки.

Если файлы не указаны в командной строке, awk читает из стандартного файла ввода. Стандартный файл ввода вы можете также указать с помощью "-". Например:


    awk '{print $3, $4}' файл1 -


awk читает сначала из файл1 и затем из стандартного файла ввода. Если программа является большой по объему, то удобнее использовать следующий формат:


    awk -f программа [список вводных файлов]


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


    awk -f myprogram file1


10.1.2. Поля

Обычно awk считывает одну строку или запись за один раз. Записью является последовательность символов, заканчивающаяся символом "новая строка". Затем awk разделяет каждую запись на поля. Поле не может быть пустой строкой и символом табуляции.

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


        USSR        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



Этот файл данных удобен для обработки - смесь слов и цифр, разделенных на поля символами "пробел" и "табуляция".

Число полей в записи определяется полем разделителем. Поля обычно разделяются последовательностью пробелов и/или табуляцией, так что первая запись countries будет иметь 4 поля, вторая - 5 и т.д. Возможно установить разделитель поля точно на символ табуляции, так что каждая строка будет иметь 4 поля, и можно искать значение данных.

При описании по умолчанию будем использовать: поля разделяются табуляцией или пробелами; первое поле в строке обозначается $1, второе - $2 и т.д. Вводная запись обозначается $0.


10.1.3. Печать



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


    {print}


так что командная строка:


    awk '{print}' countries 


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


    {print $1, $3}


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

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

[newpage=Печать форматированного файла]

10.2. Печать форматированного файла

Для вывода форматированного файла awk обеспечивает Си-подобное выражение printf:


    printf format, expr1, expr2, ..., exprn


которое печатает expr в соответствии со спецификацией в строке format. Например, программа awk:


    {print "%10s %6d\n", &1, $3}


печатает первое поле ($1) как строку из 10 символов, затем пробел, третье поле ($3) как десятичное число в шестисимвольном поле, затем новая строка (\n).

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


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


printf не проставляет автоматически в выводном файле разделителей. Вы должны создать их сами, указав "\n" в формате спецификации.

10.3. Простые шаблоны

Вы можете выбрать определенные записи для печати или другой обработки с использованием простых шаблонов. awk имеет три вида шаблонов. Первое - это шаблоны, называемые выражениями отношений, которые проводят сравнения. Например, оператор "==" тестирует на равенство. Чтобы напечатать строки, для которых 4-е поле равно строке "Asia", можно использовать программу, состоящую из одного шаблона:


    $4 == "Asia"


Если в качестве вводного файла взять countries, то получим:


        USSR    8650    262     Asia
        China   3692    866     Asia
        India   1269    637     Asia


Для сравнения используются: >, >=, <, <=, ==, != (не равно). Сравниваться могут числа и строки. Например, из нашего файла мы хотим распечатать только страны, население которых более 100 млн. Для этого введем:


    $3 > 100


Получим печать всех строк, в которых третье поле более 100.

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


    /US/


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


        USSR    8650    262     Asia
        USA     3615    219     North America


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


        BEGIN   {print "Countries of Asia:"}
        /Asia/  {print "          ", $1}


На выходе получим:


    Countries of Asia:
                USSR
                China
                India 



10.4. Простые действия

В этом подразделе описаны встроенные и определенные пользователем переменные и функции простых действий.

10.4.1. Встроенные переменные

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


    {print NR, NF}


печатает номер каждой строки и число полей в этой записи. А программа:


    {print NR, $0}


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

10.4.2. Определенные пользователем переменные

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


        {sum = sum + $3}
        END {print "Общее количество населения", sum, "млн"
            {print "Среднее количество населения", NR,
            "стран", sum/NR}


Первым действием является накопление значений из третьего поля каждой строки; второе действие, выполняемое после последнего ввода, - напечатать сумму и среднее значение:


      Общее количество населения       2201 млн
      Среднее количество населения
            10 стран                   220.1



10.4.2.1. Функции

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

[newpage=Примеры некоторых полезных программ]

10.5. Примеры некоторых полезных программ

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

Напечатать последнее поле каждой вводной строки:


    {print $NF}


Напечатать 10-ю вводную строку:


    NF == 10


Напечатать последнюю вводную строку:


    {line = $0}
    END {print line}


Напечатать строки, которые не имеют 4-го поля:


    NF != 4 {print $0, "не имеют 4-го поля" }


Напечатать вводные строки, которые имеют более 4-х полей:


    NF > 4


Напечатать последние поля вводных строк, начиная с 5-го:


    $NF > 4


Напечатать общее число вводных строк:


    END {print NR}


Напечатать обшее число полей:


        {nf = nf+NF}
    END {print nf}


Напечатать общее число символов вводного файла:


        {nc = nc + length($0)}
    END {print nc + NR}


Напечатать общее число строк, которые содержат строку "Asia":


       /Asia/   {nlines++}
    END {print nlines}


(nlines++ имеет тот же эффект, что и nlines = nlines+1).

10.6. Сообщения об ошибках

Если вы сделаете ошибку в программе awk, то получите сообщение об ошибке. Например, если вы попытаетесь запустить программу:


    $3 < 200 { print ($1}


то получите сообщение об ошибке:


        awk: syntax error at source line 1
        contex is
             $3 < 200 {print >>>$1}<<<
        awk: illegal statement at source line 1
             1 extra (


Некоторые ошибки могут быть обнаружены во время работы программы. Например, если вы попытаетесь поделить на 0 (ноль), то awk остановит обработку и распечатает номер записи вводного файла (NR) и номер строки в программе.

[newpage=Шаблоны]

10.7. Шаблоны

В выражении шаблон-действие, шаблон служит для выбора записей, для которых выполняется соответствующее действие.

10.7.1. Шаблоны BEGIN и END

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

Следующая awk-программа использует BEGIN для установки в качестве разделителя символа табуляции (\t) и создания заголовков в выводном файле. Поле-разделитель хранится во встроенной переменной FS. Хотя FS может быть восстановлено в любом месте, благоразумнее поместить в секции BEGIN, до того как вводной файл начнет считываться. Второй printf в программе выполняется для каждой вводной строки и формирует выводной файл в виде таблицы, где вся информация располагается по колонкам с заголовками. END печатает результат. (Обратите внимание, что длинная строка может быть продолжена на другой строке после запятой).


       BEGIN { FS = "\t"
                printf "%10s %6s %5s   %s\n",
                "COUNTRY", "AREA", "POP", "CONTINENT" }
                printf "%10s %6s %5s   %s\n", $1, $2, $3, $4
                area = area + $2; pop = pop + $3}
       END {printf "\n%10s %6d  %5d\n", "TOTAL", area, pop }


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


          COUNTRY   AREA     POP     CONTINENT
             USSR   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

            TOTAL  30292    2201



10.8. Выражения отношения

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

Таблица 21
Значения операторов


Оператор Значение
< Меньше чем
<= Меньше или равно
== Равно
!= Не равно
>= Больше или равно
> Больше чем
~ Входит
!~ Не входит


При сравнении, если оба операнда являются цифровыми, то проводится цифровое сравнение; в противном случае - строчное. Например, шаблон:


    $3 > 100


выбирает строки в которых третье поле больше 100, а программа:


    $1 >= "S"


выбирает строки, которые начинаются с буквы S по букву Z:


        USSR    8650    262     Asia
        USA     3615    219     North America
        SUDAN    986     19     Africa


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


    $1 == $4


сравнивает 1-е и 4-е поля как строки символов и для файла countries получим следующий результат:


    Australia 2968 14 Australia



[newpage=Регулярные выражения]

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

awk обеспечивает более мощные шаблоны для поиска строки символов, чем сравнение. Такие шаблоны называются регулярными выражениями. Простейшим регулярным выражением является строка символов, обрамленная знаками "/". Например:


    /Asia/


Эта программа печатает все записи, которые содержат подстроку Asia (запись, содержащая Asia как часть длинной строки, подобной Asian или Pan-Asiatic, также печатается).

Чтобы ограничить поиск только для специального поля, вы можете использовать операторы ~(входит) и !~(не входит). Программа:


    $4 ~ /Asia/ { print $1 }


печатает первое поле всех тех строк, в которых четвертое поле - Asia, в то время как программа:


    $4 !~ /Asia/ { print $1 }


печатает первое поле всех тех строк, в которых четвертое поле - не Asia.

В регулярном выражении могут использоваться метасимволы:


    \, ^, $, ., [, ], *, ?, (, ), |


которые подобны метасимволам, используемым в shell. Например, метасимволы "^" и "$" осуществляют поиск соответственно начала и конца строки, а метасимвол "." ищет одиночный символ. Например:


    /^.$/


просматривает все записи для поиска записи, состоящей из одного символа.

Если группа символов заключена в квадратные скобки, то это означает поиск одного символа из этой группы. Например, /[ABC]/ - осуществляет поиск либо символа "A", либо "B", либо "C". Границы букв или цифр могут быть обозначены внутри квадратных скобок:


    /[a-zA-Z]/


Если первым символом после "[" является символ "^", то это означает: любой символ, не входящий в набор. Например:


    /[^a-zA-Z]/


означает поиск любого символа, кроме буквы.

Символ "+" означает "один или больше". Например, программа:


    $2 !~ /^[0-9]+$/


печатает все записи, в которых второе поле не является строкой из одной или более цифр. (^ - начало строки, [0-9]+ - одна или более цифр, $ -конец строки).

Круглые скобки используются для группирования символов, а символ | для указания альтернативы. Программа:


    /(apple|cherry) (pie|tart)/


осуществляет поиск строк, содержащих одну из 4-х подстрок:


        apple pie
        apple tart
        cherry pie
        cherry tart 


Чтобы отменить специальное значение метасимвола, поставьте знак "\" перед ним. Например:


    /b\$/


печатает все строки, содержащие символ "b" и следующий за ним знак "$".

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

\b - возврат
\f - перевод формата
\n - новая строка
\r - возврат каретки
\t - табуляция
\ddd - восьмиричное значение
\" - кавычки
\c - с

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


    /\t/


awk интерпретирует любую строку или переменную справа от символа "~" или "!~" как регулярное выражение. Например, мы можем записать программу:


    $2 !~ /^[0-9]+$/


как


      BEGIN { digits = "^[0-9]+&" }
         $2 !~ digits 


Предположим, что нужно найти строку символов, подобную ^[0-9]+$. Если строка "^[0-9]+$" используется как регулярное выражение, появляются дополнительные знаки "\", которые защищают регулярное выражение. Это связано с тем, что первый уровень знаков "\" удаляется при синтаксическом анализе строки. Первый уровень "\" перед символом возвращает его специальное значение в регулярном выражении, второй нужен, чтобы защитить его в строке.

Например, нужно найти строки, содержащие "b" и следующий за ним знак "$". Регулярное выражение для этого шаблона:


    b\$


Чтобы создать строку для представления этого регулярного выражения, необходимо добавить еще один символ "\":


    "b\\$"


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


        x ~ "b\\$"      x ~ /b\$/
        x ~ "b\$"       x ~ /b$/
        x ~ "b$"        x ~ /b$/
        x ~ "\\t"       x ~ /\t/


Регулярные выражения и подстроки, поиск которых они осуществляют, приведены в табл. 22.

Унарные операции *, +, ? имеют наивысший приоритет, затем конкатенация и затем альтернативный выбор |.

Таблица 22
Регулярные выражения


Выражение Действие
с Любой, отличный от "с" мета-символ
Символ "с"
^ Начало строки
$ Конец строки
. Любой символ, кроме новой строки
[s] Любой символ из набора "s"
[^s] Любой символ, не входящий в набор "s"
r* Ноль или больше
r+ Один или больше
r? Ноль или один
(r) r
r1r2 Соединить r1 и r2
r1|r2 r1 или r2


[newpage=Комбинация шаблонов]

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

Составной шаблон комбирирует простые шаблоны с логическими операторами "||" (или), "&&" (и), !(отрицание). Например, нужно напечатать все страны в "Asia" с населением более 500 млн. Следующая программа выполняет выбор всех строк, у которых 4-е поле "Asia" и третье поле превышает 500:


    $4 == "Asia" && $3 > 500


Программа:


    $4 == "Asia" || $4 == "Africa"


выбирает строки с названиями "Asia" или "Africa" в 4-м поле. Эти же действия можно выполнить с помощью регулярного выражения и альтернативного оператора "|":


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


Оператор отрицания "!" имеет более высокий проиритет, чем "&&" и "||". Операторы "&&" и "||" вычисляются слева направо. Вычисление останавливается как только истина или ложь будут достигнуты.

10.11. Область шаблона

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


    pat1, pat2 {...}


В этом случае действие выполняется для каждой строки, расположенной между pat1 и pat2 (включительно). Например, шаблон:


    /Canada/, /Brazil/


ищет строки со словом "Canada" до строки со словом "Brazil"


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



Также, если FNR - число текущих записей в текущем вводном файле, FILENAME - имя текущего вводного файла, то программа:

FNR == 1, FNR == 5 {print FILENAME, $0}
печатает первые 5 записей каждого вводного файла с именем FILENAME.

[newpage=Действие]

10.12. Действие

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

Встроенные переменные

В табл. 23 приведены встроенные переменные, которые поддерживает awk.

Таблица 23
Встроенные переменные


Переменная Значение Умолчание
ARGC Число аргументов команд ной строки -
ARGV Массив аргументовной строки командной строки -
FILENAME Имя текущего вводного файла -
FNR Номер записи в текущем файле -
FS Поле разделителя вводного файла пробел и/или табуляция
FN Число полей в текущей записи -
NR Число считанных на данный момент записей -
OFMT Выводной формат для цифр %.6g
OFS Разделитель поля выводного файла пробел
ORS Разделитель записи выводного поля символ новой строки
RS Разделитель записи вводного файла то же
RSTART Индекс первого выбранного символа при помощи match() -
RLENGTH Длина строки, выбранной при помощи match() -
SUBSEP Нижний разделитель "\034"


10.12.1. Арифметические действия

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


    100 * $3 / $2


дает плотность населения на 1 квадратный километр.

Программа:


      {print "%10s %6.1f\n", $1,
        1000 *$3 / $2}


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


             USSR    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


Арифметические действия выполняются с плавающей точкой. Арифметическими операторами являются:


    +, -, *, /, %, ^


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


    v = e 


где v - переменная или имя поля;
e - выражение.

Например, чтобы вычислить число стран континента "Asia" и общее количество населения, вы должны написать:


        $4 == "Asia"  { pop = pop = $3; n = n + 1 }
        END           { print "population of", n,
              "Asian countries in million is", pop }


Относительно файла countries эта программа выдает результат:


        population of 3 Asian countries in
        million is 1765


Действие, связанное с шаблоном $4 == "Asia" выполняет 2 назначения, одно - накопление населения и другое - подсчет стран.

Назначения в предыдущей программе могут быть записаны более сжато с использованием операторов "+=" и "++":


    $4 == "Asia" {pop += $3; ++n}


Оператор "+=" заимствован из языка программирования Си, следовательно:


    pop += $3


аналогично:


    pop = pop + $3


но оператор "+=" короче и работает быстрее.

Операторами назначения являются:


    +=, -=, *=, /=, %=, ^=


Операторами приращения являются "++" и "--". Как и в языке Си они могут использоваться как префиксные (++x) или постфиксные (x++) операторы. Если x равно 1, то "i = ++x" увеличивает x, затем устанавливает i равным 2, в то время как "i = x++" устанавливает i равным 1, затем увеличивает x. Аналогичная интерпретация для префиксного и постфиксного операторов "--".

Операторы присвоения, увеличения и уменьшения могут использоваться в арифметических выражениях.

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


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


Обратите внимание, что эта программа будет некорректна, если значение $3 будет отрицательным.

В табл. 24 перечислены встроенные арифметические функции.

Таблица 24
Встроенные арифметические функции


Функция Возвращаемое значение
atan2(y,x) Арктангенс y/x в пределах от "-пи" до "пи"
cos(x) Косинус x
exp(x) Экспоненциальная функция x
int(x) Целая часть x с усеченными лидирующими нулями
log(x) Натуральный логарифм x
rang() Случайное число между 0 и 1
sin(x) Синус x
sqrt(x) Квадрат x
srand(x) x - новое начальное значение для rand()


Функция rand() возвращает псевдослучайное число с плавающей точкой в диапазоне от 0 до 1, а srand(x) может быть использовано для установки нового начального значения генерирующей программы. Если srand() не имеет аргументов, то начальное значение производится из времени дня.

[newpage=Строки и строковые функции]

10.13. Строки и строковые функции

Строка констант - это последовательность символов, заключенная в двойные кавычки, как например, "abc", "hello, everyone".Строкаконстант может содержать последовательности escape языка программирования Си для специальных символов.

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

Программа:


    { print NR ":" $0 }


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

В табл. 25 приведены встроенные строковые функции, поддерживаемые awk. В этой таблице r представляет собой регулярное выражение (либо как строка, либо как /r/), s и t - строковые выражения, n и p - целые числа.

Таблица 25
Встроенные строковые функции awk


Функция Описание
gsub(r, s) Глобальная замена s на r в текущей записи; возвращает количество земененых символов
gsub(r,s, t) Глобальная замена s на r в строке t, возвращает количество замененных символов
index(s,t) Возвращает позицию t в s: 0 - если t нет в s
length(s) Возвращает длину s
matgch(s,r) Возвращает позицию s, в которой встречается r; 0 - если r не встретилось
split(s,a) Разделяет s на массив a по FS; возвращает число полей
split(s,a,r) Разделяет s на массив a по r; возвращает число полей
sprintf(fmt,expr-list) Возвращает expr-list, отформатированный в соответствии с форматом строки fmt
sub(r,s) Замещает s на первое r в текущей записи, возвращает количество замен
sub(r,s,t) Заменяет s на первое r в строке t, возвращает количество замен
substr(s,p) Возвращает индекс s, начиная с позиции p
substr(s,p,n) Возвращает подсказку s длиной n, начиная с позиции p


Функции sub и gsub сформированы после команды замены в текстовом радакторе ed. Функция gsub(r,s,t) заменяет успешное появление подстрок, найденных при помощи регулярного выражения r с заменой строки s в целевой строке t. Функция gsub(r,s) является синонимом gsub(r,s,$0). Например, программа:


    { gsub(/USA/, "United States"); print }


преобразует ввод, меняя появление "USA" на "Unites States". Функция sub подобна ей, за исключением того, что она заменяет первую найденную подстроку в целевой строке.

Функция index(s,t) возвращает левую крайнюю позицию, с которой строка t начинается в s. Первый символ в строке начинается с позиции 1. Например,


    index("banana", "an")


возвращает 2.

Функция length возвращает число символов в строке; так:


    { print length($0), $0 }


печатает каждую запись, а перед ней ее длину. ($0 не включает в вводную запись разделитель). Программа:


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


применительно к файлу countries распечатывает наибольшее имя страны:


    Australia


Функция match(s,r) возвращает позицию в строке s, в которой появилось регулярное выражение r, либо 0, если оно не найдено. Эта функция также устанавливает две встроенные переменные RSTART и RLENGTH. RSTART принимает значение начальной позиции, найденной в строке, это значение равно возвращаемому значению. RLENGTH принимает значение длины найденной строки. (Если строка не найдена, то RSTART равно 0, а RLENGTH равно -1). Например, следующая программа ищет появление буквы i и за ней сразу или через один символ следует буква a:


           { if (match($0, /i.?a/))
                { print RSTART, RLENGTH, $0 }


Относительно файла countries получим следующий вывод:


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


Функция sprintf(format, expr1, expr2, ..., exprn) возвращает (без печати) строку, содержащую expr1, expr2, ..., exprn, отформатированную в соответствии со спецификацией printf в строке format. Выражение:


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


присваивает x строку, полученную при форматировании $1 и $2 как 10-символьных строк и десятичное число в поле шириной как минимум 6 знаков.

Функция substr(s,p,n) возвращает подстроку s, которая начинается с позиции p и имеет длину не менее n символов. Если используется функция substr(s,p), то подстрока направляется в конец s, так что она состоит из индекса s, начинающегося с позиции p. Например, мы можем сократить имена стран в файле countries до трех символов, вызвав программу:


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


В итоге получим:


        USS 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


Обратите внимание, что установка $1 в программе приводит к тому, что awk заново вычисляет $0 и, кроме того, поля разделяются пробелами (значение по умолчанию для OFS), но не табуляцией.

Чтобы слить строки, надо просто записать их одна за другой. Например, для файла countries:


        { s = s substr($1, 1, 3) " " }
        END { print s }


печатает:


        USS Can Chi USA Bra Aus Ind Arg Sud Alg



[newpage=Поле переменных]

10.14. Поле переменных

Поля текущей записи могут ссылаться на поле переменных $1, $2, ... $NF. Эти переменные могут использоваться в арифметических или строковых операциях, им могут присваиваться различные значения. Например, вы можете разделить второе поле файла countries на 1000, чтобы площадь измерялясь не в тысячах, а в миллионах квадратных метров:


    { $2 /= 1000; print }


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


        BEGIN                  { FS = OFS = "\t" }
        $4 == "North America"  { $4 = "NA" }
        $4 == "South America"  { $4 = "SA" }
                               { print }


Действие BEGIN устанавливает поле разделителя вводного файла (FS) и поле разделителя выводного файла (OFS) в значение табуляции. Обратите внимание, что print в четвертой строке программы печатает значение $0 после того как оно было модифицировано предыдущими присвоениями.

К полям можно получить доступ при помощи выражений. Например,


    $(NF-1)


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

Поле переменных, ссылающееся на несуществующее поле, например, $(NF+1), имеет в качестве своего начального значения пустую строку. Тем не менее новое поле может быть создано при присвоении ему значения. Например, следующая программа, вызвавшая файл countries, создает пять полей, дающих плотность населения:


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


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

10.15. Номер или строка

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


    pop += $3


pop и $3 должны трактоваться как цифровые.

В контексте строки, подобной:


    print $1 ":" $2


строки $1 и $2 сливаются.

В операторах присвоения "v = e" или "op = e" тип "v" станет таким же, как и у "e". В двухсмысленном контексте:


    $1 == $2


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

Если два операнда являются цифровыми, то сравнение будет цифровым, если операнды являются строковыми, то сравнение - строковое. Все поля переменных имеют строковый тип; дополнительно каждое поле, которое содержит только цифры, имеет цифровой тип. Например, сравнение "$1 == $" успешно выполнится для любой части ввода:


    1 1.0 +1 0.1e+1 10E-1 001


но неуспешно для:


        (ноль)  0
        (ноль)  0.0
        0a      0
        1e50    1.0e50


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

number"" - присоединяет нулевую строку к number для приведения к строковому типу;
string+0 - добавляет ноль к string для приведения к цифровому типу.

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


    $1 "" == $2 ""


Так значение: "12.34x" равно 12.34, а значение "x12.34" равно нулю.

Значение строки арифметического выражения вычисляется путем формирования строки с преобразованием формата выводного файла.

Неустановленные переменные имеют цифровое значение 0 (ноль) и строковое значение "".

Несуществующие поля имеют только строковое значение ""; они не являются цифровыми.

[newpage=Операторы управления потоком]

10.16. Операторы управления потоком

awk поддерживает операторы if-else, while, do-while аналогично языку программирования Си.

Синтаксис оператора if:


    if (выражение) оператор_1 else оператор_2


"выражение" является условным и не имеет ограничений. Оно может включать операторы отношений:


    <, <=, >, >=, ==, !=


регулярные выражения:


    ~, !~


логические операторы:


    ||, &&, !


операторы слияния и круглые скобки для группирования.

В операторе if awk сначала вычисляет "выражение". Если оно не ноль и не пустое, то оператор_1 выполняется, в противном случае выполняется оператор_2. Часть else не является обязательной.

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

Возьмем файл countries и вычислим максимальное население с помощью оператора if:


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


Синтаксис оператора while:


    while (выражение) оператор 


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


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


Синтаксис оператора for:


    for(выражение_1; выражение; выражение_2) оператор


Он аналогичен следующей последовательности:


        выражение_1
        while ( выражение) {
              оператор
              выражение_2
        }


Синтаксис оператора do:


    do оператор while (выражение)


Оператор выполняется до тех пор, пока "выражение" не станет равным нулю. Тестирование проводится после выполнения "оператора", т.е. в конце цикла. Как правило оператор do используется реже, чем while или for.

Оператор break приводит к немедленному выходу из while или for; чтобы продолжить оператор, надо начать новую итерацию.

Следующий оператор заставит awk перейти к новой записи и начать поиск шаблона, начиная с первого оператора "шаблон-действие".

Оператор exit завершает программу; ввод больше не считывается и действие END выполняется, если оно есть в программе.


    exit expr 


приводит к тому, что программа возвращает значение "expr" как состояние выхода. Если "expr" в строке нет, то состояние exit равно нулю.

[newpage=Массивы]

10.17. Массивы

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


    x[NR] = $0


присваивает текущую строку вводного файла элементу NR массива x.

Фактически возможно считать целый вводной файл в массив с помощью программы awk:


              { x[NR] = $0 }
        END   { ... обработка ...}


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

Элементы массива могут именоваться с помощью нецифровых величин. Например, следующая программа накапливает общее количество населения Asia и Africa в соответветствующий массив pop. Оператор END печатает общее количество населения этих двух континентов.


        /Asia/          { pop["Asia"] += $3 }
        /Africa/        { pop["Africa"] += $3 }
 END {     print "Asian population in million is", pop[Asia]
       print "African population in million is", pop[Africa]
     }


Результат получим следующий:


      Asian population in million is   1765
      African population in million is   37


В этой программе, если вы воспользуетесь pop[Asia] вместо pop["Asia"], то выражение будет использовать значение переменной как индекса, и так как значение переменной не установлено, то количество населения будет накапливаться в pop[""].

Предположим, нужно определить общую площадь каждого континента из файла countries.

Каждое выражение может быть использовано как индекс при ссылке в массиве. Так:


    area[ $4 ] += $2


использует строку в 4-м поле текущей записи вводного файла для индексирования массива area, накапливая значение второго поля:


        BEGIN  { FS = "\t" }
               { area[$4] += $2 }
        END    { for (name in area)
                     print name, area[name] }


Относительно файла countries получим результат:


          Asia  13611
          North America 7467
          South America 4358
          Australia 2968
          Africa 1888


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


    for ( i in array ) оператор


выполняется "оператор" с переменной i , для которой определен array(i). Цикл выполняется для каждого определенного индекса, который выбирается в произвольном порядке.

awk не поддерживает многомерные массивы, но допускает список индексов. Они объединяются в один индекс значениями, разделенными строкой (хранимой в переменной SUBSEP).

Например:

for ( i = 1; i <= 10; i++ )
for ( j = 1; j <= 10; j++ )
arr[i]

 = ...


создает массив, который ведет себя как двумерный массив. Индексом является сочетание i, SUBSEP и j.

Вы можете определить, появляется ли конкретное i в массиве arr:


    if ( "Africa" in arrea ) ...


Это условие приведет к выполнению тестирования без создания массива ["Africa"]. Этот массив создался, бы если использовалось


    if ( area ["Africa"] != "" ) ...


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


    split ( "s1:s2:s3", a, ":" )


split разбивает строку на 3 поля, используя в качестве разделителя ":" и сохраняя s1 в [1], s2 - в [2], s3 - в [3]. Возвращаемое значение этого оператора равно числу полей, т.е. трем. Третий аргумент функции split - это регулярное выражение, будет использоваться как поле разделителя. Если третий аргумент отсутствует, то в качестве поля разделителя будет использоваться FS.

Массив элементов может быть разделен с помощью аргумента delete:


    delete имя_массива [индекс]



10.18. Функции, определенные пользователем

awk поддерживает функции, определенные пользователем:


         function имя( список_аргументов) {
                      операторы
         }


Это определение может появляться в любом месте, где возможен оператор "шаблон-действие". Список аргументов - это список имен переменных, разделенных запятыми. Внутри тела функции эти переменные ссылаются на действительные переменные при вызове функции. Между именем функции и левой круглой скобкой не должно быть пробела, иначе это будет означать конкатенацию.

Массив элементов просматривается при обращении, как и в Си. Функция при просмотре не может изменить значение скалярных аргументов. Внутри функции формальные параметры являются локальными переменными, но все другие переменные являются глобальными. У вас может быть любое количество формальных параметров, которые используются только как локальные переменные. Операторreturnявляется необязательным, но если он отсутствует, возвращаемое значение будет неопределенным.

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

В программе awk могут присутствовать комментарии. Они начинаются с символа # и заканчиваются символом новой строки:


    print x,y # это комментарий 


Операторы обычно занимают одну строку. Несколько операторов могут располагаться на одной строке, тогда они должны разделяться точкой с запятой. Длинный оператор может располагаться на нескольких строках, причем каждая продолжаемая строка должна заканчиваться символом "\". Нельзя продолжить строку вида "....". Такое продолжение встречается редко, однако как только строка заканчивается запятой, операторы продолжаются автоматически. Примером этого служат операторы print и printf, и такое возможно после операторов "&&" и "||". Несколько операторов "шаблон-действие" могут появляться на одной строке, если они разделены точкой с запятой.

[newpage=Вывод]

10.20. Вывод

Операторы print и printf являются простейшими конструкциями, которые генерируют вывод. Оператор print используется для образования просто вывода; printf - для форматируемого вывода. Подобно shell awk позволяет вам перенаправлять вывод в файл или в канал.

10.20.1. Оператор print

Оператор:


    print expr1, rxpr2, ..., exprn 


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

Оператор:


    print 


является сокращенной формой оператора:


    print $0


Чтобы напечатать пустую строку, введите:


    print ""


10.20.2. Разделители вывода

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


        BEGIN   { OFS = ":"; ORS = "\n\n" }
                { print $1, $2 }


Обратите внимание, что :


    { print $1 $2 }


печатает первое и второе поле без разделителя полей вводного файла.

10.20.3. Оператор printf

Оператор printf, используемый в awk, подобен этому оператору в Си, за исключением того, что спецификатор * не поддерживается.

Общий формат оператора:


    print format expr1, rxpr2, ..., exprn 


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

Каждая спецификация начинается с символа "%", заканчивается буквой, которая определяет преобразование, и может включать:
- - выравнивание по левому краю в поле;
width - заполнить поле на заданную ширину; поля, начинающиеся с лидирующего нуля, должны быть заполнены нулями;
.prec - указывает максимальную ширину строки или разряд справа от десятичной точки.

В табл. 26 приведен список символов преобразования printf.

Таблица 26
Символы преобразования printf


Символ Вид печати выражения
c Один символ
d Десятичное число
e [-]d.ddddddE[+-]dd
f [-]ddd.dddddd
g e или f преобразование с подавленем незначащих нулей
o Беззнаковое восьмиричное число
s Строка
x Беззнаковое шестнадцатиричное число
% Печать %; нет аргументов для преобразования


Примеры оператора printf с соответтвующим выводом на той же строке.


        printf "%d", 99/2                     49
        printf "%s", 99/2                     4.950000e+01
        printf "%f", 99/2                     49.500000
        printf "%6.2f", 99/2                   49.50
        printf "%g", 99/2                      49.5
        printf "%o", 99                        143
        printf "%06o", 99                      000143
        printf "%x", 99                        63
        printf "|%10s|", "January"             |   January|
        printf "|%-10s|", "January"            |January   |
        printf "|%.3s|", "January"             |Jan|
        printf "|%10.3s|", "January"           |       Jan|
        printf "|%-10.3s|", "January"          |Jan       |
        printf "%%"                            %


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

10.20.4. Вывод в файлы

В качестве стандартного вывода для печати можно использовать файлы. Для этого используются операторы изменения направления > и >>. Например, следующая программа вызывает печать из файла countries всех строк, в которых 3-е поле (население) больше, чем 100, в файл bigpop и все остальные строки в файл smallpop:


        $3 > 100     { printf $1, $3 > "bigpop"  }
        $3 > 100     { printf $1, $3 > "smallpop"  }


Обратите внимание, что имена файлов заключены в двойные кавычки; без кавычек bigpop и smallpop будут просто неустановленными переменными. Если имена выводных файлов создаются с помощью выражения, они должны быть заключены в круглые скобки:


    $4 ~ /North America/ { print $1 > ("tmp" FILENAME) }


так как оператор > имеет более высокий приоритет, чем конкатенация. Без круглых скобок конкатенация tmp и FILENAME не будет производиться.

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

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

Вы можете направить печать в канал.

Оператор:


    print | "командная_строка"


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

Предположим, вы хотите создать список страна-население так, чтобы страны были отсортированы по алфавиту. Программа awk накапливает значение "количество населения" из 3-го поля для каждого названия страны из 4-го поля и вывод направляет в массив pop. Затем print название каждой страны и ее население направляет вывод команде sort:


        BEGIN    { FS     "\t"  }
                 { pop [$4] += $3 }
        END      { for ( c in pop )
                   print c ":" pop[c] | "sort" }


В результате работы этой программы получим:


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



Во всех этих операторах print, вызывающих перенаправление вывода, файлы или каналы идентифицируются с помощью имен (так, канал в данном примере называется "sort"), но они создаются и открываются один раз при запуске. Так что в последнем примере для всех "c" в "pop" открывается только один канал "sort".

Существует ограничение на число файлов, которые могут быть открыты одновременно. Оператор close (файл) закрывает файл или канал.

Когда открывается или закрывается файл, различные строки являются различными командами.

[newpage=Ввод]

10.21. Ввод

Наиболее общим способом подачи ввода программе awk является указание в командной строке имен вводных файлов. Но существуют и другие способы. Они описываются в этом подразделе.

10.21.1. Файлы и каналы

Вы можете поместить вводимые данные в файл, указать awkdata и затем выполнить:


    awk 'программа' awkdata


Если не указано имя файла, то awk читает из стандартного файла ввода. Например, egrep выбирает вводные строки, содержащие указанное регулярное выражение, которое может сделать это быстрее, чем awk, т.к. выполняет только это действие. И мы можем вызвать кроме того канал:


    egrep 'Asia' countries | awk '...'


egrep быстро находит строки, содержащие "Asia" и затем направляет их программе awk для последующей обработки.

10.21.2. Разделители ввода

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


           поле 1 поле 2
        поле 1
     поле 1


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

Разделитель поля может быть установлен при помощи регулярного выражения при присваивоении значения встроенной переменной FS.

Например:


    BEGIN { FS = ",[\t] * | ([\t]+" }


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

Пример.


    awk -F ' (, [\t]*) | ([\t]+)' '...'


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

Регулярные выражения используются как разделители поля для поиска самых длинных строк (как в sub()), но не нулевых строк.

[newpage=Многострочные записи]

10.22. Многострочные записи

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


    BEGIN { RS = "" }


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


    BEGIN { RS = ""; FS = "\n" }


установка в качестве разделителя записи символ пустой строки и разделителя поля - символ новой строки. Таким образом, каждая строка является одним полем. Однако длина записи ограничена (обычно 2500 символов).

10.23. Функция getline

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

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

getline вызывает следующую вводную запись и выполняет над ней нормальную операцию по разбиению на поля. Он устанавливает NF, NR, FNR. getline возвращает 1, если запись существует, 0 - если найден конец файла и -1, если появляется ошибка. (Например, невозможно открыть файл).

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


    f[1] f[2] ... f[nf]


Как только встретится строка, содержащая STOP, то запись может быть обработана в виде данных массива f:


        /^START/  {
                   f[nf-1] = $0
                   while (getline && $0 !~ /~STOP/ }
                          f[++nf] = $0
                    # now process the data in f[1] ... f[nf]
                    ...
        }


Обратите внимание на тот факт, что && вычисляет операнды слева направо и останавливает вычисление как только один из них будет истинным.

То же самое задание может быть выполнено с помощью следующей программы:


        /^START/ && nf == 0   { f[nf-1] = $0 }
        nf > 1                { f[++nf] = $0 }
        /^STOP/    # now process the data in f[1] ... f[nf]
                               ...
                               nf = 0
        }


Оператор:


    getline x 


читает из файла вместо текущего ввода. Значение NR и FNR не устанавливается, но разбиение полей выполняется и устанавливается значение NF.

Оператор:


    getline x < "file"


получает следующую запись из файла и направляет в x; разбиение не производится и значение NF, NR и FNR не устанавливается.

Если имя файла является выражением, то оно должно быть заключено в круглые скобки для вычисления:


    while ( getline x < (ARGV[1] ARGV[2]) ) { ... }


т. к. операция "<" имеет больший приоритет, чем конкатенация. Без круглых скобок оператор подобный:


    getline x < "tmp" FILENAME 


указывает , что нужно читать в файл "x" из файла "tmp", а не в "tmp" <значение FILENAME>.

Если вы используете оператор, подобный:


    while ( getline x < file) { ... }


то цикл будет бесконечным, если файл не может быть считан, т.к. getline возвращает -1 в этом случае. Лучше такой тест сделать с помощью следующего оператора:


    while ( getline x < file > 0) { ... }


Вы также можете направить вывод другой команды прямо getline. Например, оператор:


    while ( "who" | getline )
                n++


выполняет who и направляет свой вывод в getline. Каждая итерация цикла while читает более одной записи и увеличивает переменную n. После завершения цикла while, n содержит количество пользователей.

Оператор:


    "date" | getline d 


направляет вывод из date в переменную d, таким образом устанавливается в d текущая дата.

В табл. 27 суммируется рассказанное в этом пункте.

Таблица 27
Функция getline


Форма Устанавливаемое значение
getline $0, NF, NR, FNR
getline var var, NR, FNR
getline < file $0, NF
getline var < file var
cmd | getline $0, NF
cmd | getline var


10.24. Аргументы командной строки

В программе awk могут использоваться аргументы командной строки: массив ARGV содержит элементы ARGV[0], ... ARGV[ARGC-1], где ARGC - счетчик, ARGV[0] - имя программы (в общем случае - awk), остальное - любые поддерживаемые аргументы, исключая программы и необязательные аргументы).

Следующая командная строка содержит программу awk, отражающую аргументы, которые появляются после имени программы:

awk '
BEGIN {
for ( i = 1; i < ARGC; i++ )
printf "%s ", ARGV[i]


                printf "\n"
        }' $*


Аргументы могут быть модифицированы или добавлены. ARGC может изменяться. После окончания вводного файла awk обращается к следующему ненулевому элементу ARGV (увеличив текущее значение ARGC-1) как к имени следующего вводного файла.

Исключением из правила является то, что аргумент считается именем файла, если он имеет форму:


    var-value 


Переменная var принимает значение value, как при операции присвоения. Если value является строкой, то кавычки не нужны.

[newpage=Использование awk с другими командами и с shell]

10.25. Использование awk с другими командами и с shell

Наибольшего эффекта awk достигает при использовании с другими программами. В этом подразделе обсуждаются некоторые способы взаимодействия программ awk с другими командами.

10.25.1. Функция system

Встроенная функция system (command_line) выполняет команду "command_line", которая может быть строкой, вычисляющей, например, sprintf. Функция system возвращает состояние выполненной команды.

Например:


             $1 == "#include" { gsub (/[<>"]/, $2;
             system ("cat " $2}


вызывает команду cat для печати файла, названного во втором поле каждой вводной записи, у которой первое поле #include, после разборки каждого <, > или ", которые должны присутствовать.

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

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


    awk '{ print $1 }' ...


Так как awk использует те же символы, что и shell (такие как $ и ", окружающие программу awk), одиночные кавычки обеспечат прохождение программы неизменной через shell к интерпретатору awk.

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


        G.  R.  Emlin
        600 Mountain Avenue
        Murray Hill, NJ 07974
        201-555-1234


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


    addr Emlin 


Это легко выполнить с помощью следующей программы:


        awk '
        BEGIN  { RS = "" }
        /Emlin/
        ' addresslist 


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

Существует несколько способов сделать это. Один из способов - это создать файл, названный addr, который содержит:


        awk '
        BEGIN  { RS = "" }
        /'$1'/
        ' addresslist


В программе awk один аргумент, хотя установлено два набора кавычек, но они не являются вложенными. $1 заключено в одиночные кавычки и видимо для shell; затем будет заменено на шаблон Emlin при вызове команды addr Emlin.

Второй способ реализации addr полагается на тот факт, что shell заменяет параметры $ в двойных кавычках:


        awk "
        BEGIN  { RS = \"\" }
        /$1/
        " addresslist


Кроме того, вы должны защитить кавычки, определяющие RS символами \, так что shell направит их awk без интерпретации. $1 распознается как параметр и shell заменяет его на шаблон, когда команда addr вызывается с шаблоном.

Третий способ реализации addr - использовать ARGV для передачи регулярного выражения программе awk, которая читает список адресов с помощью getline:


        awk '
        BEGIN   { RS = ""
                 while ( getline < "addresslist" )
                   if ($0 ~ ARGV[1]
                      print $0
        } ' $*


Вся обработка выполняется в "действии" оператора BEGIN.

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

[newpage=Примеры использования]

10.26. Примеры использования

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

10.26.1. Генерирование отчетов

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


        Africa:
                Sudan            19
                Algeria          18
        Asia:
                China           866
                India           637
                USSR            262
        Australia:
                Australia        14
        North America:
                USA             219
                Canada           24
        South America:
                Brazil          116
                Argentina        26


Так как здесь несколько задач обработки данных, то намного легче выполнить этот отчет в несколько стадий. Первая: создать список троек "континент_страна_население", в котором каждое поле отделяется запятой. Это можно сделать с помощью следующей программы triplies, которая использует массив pop, индексированный в форме "континент:страна" для сохранения количества населения данной страны. Оператор print в секции END этой программы создает список троек "континент-страна-население", который направляется в программу sort:


        BEGIN  { FS = "\t" }
               { pop[$4 ":" $1] += $3 }
        END    { for ( cc in pop )
           print cc "":" pop[cc] | "sort -t: +0 -1 +2nr"  }


Аргумент для sort заслуживает специального внимания. Аргумент -t: говорит sort, чтобы использовать ":" как разделитель полей. Аргументы +0 и -1 делают первое поле первичным ключом sort. В общем случае +i -j делают поля i+1, i+2, ... j ключом сортировки. Если j опущено, поля от i+1 до конца записи используются. Аргумент +2nr делает третье поле (цифровое уменьшение) вторичным ключом sort ( n - числовое, r - обратный порядок). Относительно файла countries эта программа выдает результат:


        Africa:Sudan:19
        Africa:Algeria:18
        Asia:China:866
        Asia:India:637
        Asia:USSR:262
        Australia:Australia:14
        North America:USA:219
        North America:Canada:24
        South America:Brazil:116
        South America:Argentina:26


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


        BEGIN  {  FS = ":" }
        {      if ($1 != prev) {
                   print "\n" $1 ":"
                   prev = $1
                }
                printf "\t%-10s %6d\n", $2, $3
        }


Эта программа прерывания управления печатает только первое появление имени континента и форматирует строки "страна-население", соответствующие этому континенту, требуемым способом. Командная строка:


    awk -f triplies countries | awk -f format


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

10.26.2. Дополнительные примеры
10.26.2.1. Частота использования слов


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


        { for ( w = 1; w <= NF; w++ ) count[$w]++ }
  END   {for( w in count) print count[w], w | " sort -nr" }


Первый оператор использует массив count для накопления количества появлений каждого слова. Как только ввод будет считан, второй оператор цикла for направляет окончательный счетчик каждого слова команде sort.

10.26.2.2. Накопление

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


        awk '
        FILENAME == "deposits"        { balance[$1] += $2 }
        FILENAME == "withdrawals"     { balance[$1] -= $2 }
        END                        { for (name in balance )
                                  print name, balance[name]
        }' deposits withdrawals


Первый оператор использует массив balance для накопления общего количества для каждого имени в файле deposits. Второй оператор вычитает соответствующий вывод из каждого общего депозита. Оператор END печатает каждое имя с соответствующим итогом.

10.26.2.3. Случайный выбор

Следующая функция печатает случайные элементы k, начиная с первого элемента массива A, состоящего из n элементов. В программе k - это количество входов, необходимых для печати, n - количество элементов, которые еще будут исследоваться. Выбор печатать или нет i-тый элемент определяется тестом rand() < k/n:

function choose (A, k, n, i) {
for (i = 1; n > 0; i++)
if (rand() < k/n--) {
print A[i]


                        k--
                      }
                 }
        }


10.26.2.4. Возможности shell

Следующая программа awk приблизительно моделирует возможности shell системы UNIX. Строка, содержащая только знак "=" заново выполняет последнюю выполненную команду. Строка, начинающаяся с =cmd заново выполняет последнюю команду, вызов которой включает строку cmd. Иначе выполняется текущая строка.

$1 == "=" { if [NR == 1]
system ( x[NR] = x [NR-1] )
else
for ( i= NR-1]; i > 0; i-- )
if ( x[i] ~ $2 ) {
system(x[NR] = x[i]

)
                                break
                            }
                     next }
        /./          { system(x[NR] = $0) }



[newpage=Итоговое краткое описание awk]

10.27. Итоговое краткое описание awk
Командная строка



        awk programm filenames
        awk -f programm-file filenames
        awk -Fs sets field separator to string s
        awk -Ft sets separator to tab



Шаблоны



        BEGIN
        END
        /regular expression/
        relational expression
        pattern && pattern
        pattern || pattern
        (pattern)
        !pattern
        pattern, pattern



Операторы управления потоком


        if (expr) statement [else statement]
        if (subscript in array) statement [else statement]
        while (expr) statement
        for (expr; expr; expr) statement
        for (var in array) statement
        do statement while statement
        break
        cintinue
        next
        exit [expr]
        return [expr]



Ввод-вывод

close (filename) закрыть файл
getline установить $0 из следующей вводной записи; установить NF, NR, FNR
getline установить $0 из следующей записи файла; установить NF
getline var установить var из следующей вводной записи; установить NR, FNR
getline var установить var из следующей записи файла
print распечатать текущую запись
print expr-list распечатать выражение
print expr-list >file печать выражений в файл
printf fmt, expr-list отформатировать и распечатать
printf fmt, expr-list >file отформатировать и распечатать в файл
system (cmd-line) выполнить команду cmd-line, возвратить состояние


В print и printf >>file добавляется в file и |command - записывает в канал.

Функции


        func name(parameter list) { statement }
        function  name(parameter list) { statement }
        function-name(expr, expr, ...)



Функции строки

gsub(r,s,t) заменить строку s для каждого найденного регулярного выражения r в строке t; возвращает количество замен; если t опущено, то используется $0
index(s,t) возвращает индекс строки t в строке s, или 0, если нет вхождений строки t
length(s) возвращает длину строки s
match(s,r) возвращает позицию s, в которой встретилось регулярное выражение r; возвращает 0, если r не найдено
split(s,a,r) разбить строку s в массив a по регулярному выражению r; возвращает количество полей; если r опущено, то используется значение FS
sprints(fmt,expr-list) печатает expr-list в соответствии с fmt, возвращает результирующую строку
sub(r,s,t) аналогично gsub, за исключением того, что заменяется только первая найденная подстрока
substr(s,i,n) возвращает подстроку n, начинающуюся с i; если n опущено, то используется остаток s


Арифметические функции

atan2(y,x) арктангенс y/x в пределах от "-пи" до "пи"
cos(x) косинус x
exp(x) экспоненциальная функция x
int(x) целая часть x с усеченными лидирующими нулями
log(x) натуральный логарифм x
rang() случайное число между 0 и 1
sin(x) синус x
sqrt(x) квадрат x
srand(x) x - новое начальное значение для rand()


Операторы

= += -= *= /= %= ^= присвоение
?: условное выражение
|| логическое OR
&& логическое AND
~ !~ поиск регулярного выражения; отрицательный поиск
< <= > >= != == отношения
blank конкатенация строк
+ - сложить, вычесть
* / % умножить, разделить, режим
+ - ! унарный плюс, унарный минус, логическое отрицание
^ показательная функция ( ** является синонимом)
++ -- приращение, отрицательное приращение
$ поле


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

с поиск на совпадение с немета-символом "c"
поиск буквенного символа "с"
^ поиск начала строки или последовательности строк
$ поиск конца строки или последовательности строк
. поиск любого символа, кроме символа новой строки
[s] поиск любого символа из набора "s"
[^s] поиск любого символа, отличного от "s" и символа новой строки
r* поиск ноль или больше
r+ поиск одного или больше
r? поиск ноль или один
(r) группирование: поиск r
r1r2 конкатенация: поиск r1 затем r2
r1|r2 поиск либо r1 либо r2


Встроенные переменные

ARGC число аргументов командной строки
ARGV массив аргументов командной строки
FILENAME имя текущего вводного файла
FNR номер записи в текущем файле
FS разделитель поля вводного файла; (по умолчанию - пробел)
FN число полей в текущей записи
NR число считанных на данный момент записей
OFMT выводной формат для цифр; (по умолчанию - %6.g)
OFS разделитель поля выводного файла
ORS разделитель записи выводного поля
RS разделитель записи вводного файла
RSTART индекс первого выбранного символа при помощи match(); 0 - если символ не найден
RLENGTH длина строки, выбранной при помощи match() -1 - если строка не найдена
SUBSEP разделитель индексов элементов массива; (по умолчанию - \034)


10.27.1. Ограничения

При работе с awk вы должны придерживаться следующих ограничений:

100 полей
2500 символов во вводной записи
2500 символов в выводной записи
1024 символов в индивидуальном поле
1024 символов в строке printf
400 символов в строке, заключенной в кавычки
400 символов в классе символов
15 открытых файлов
1 канал


10.27.2. Инициализация, сравнение и тип приведения

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


    var = expr 


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


    v1 = v2


то типом v1 становится тип v2. При сравнении, если оба операнда являются цифровыми, то производится цифровое сравнение. В противном случае, операнды рассматриваются как строковые и сравнение производится над строками. Тип любого выражения может быть приведен к цифровому таким образом:


    expr + 0


и к строковому типу:


    expr ""


(это конкатенация с пустой строкой)

Инициализированные переменные имеют цифровое значение 0, а строковые - значение "". Соответственно, если x проинициализировано, то оператор


    if (x) ...
имеет значение "ложь", а 

        if (!x) ...
        if (x == 0) ...
        if (x == "") ...
все являются истиной, но 

        if (x == "0") ...
является ложью. 



Тип поля определяется по контексту. Например:


    $1++
означает, что $1 будет цифровым, и 

    $1 = $1 "," $2


означает, что и $1 и $2 являются строковыми. Приведение к типу выполняется при необходимости.

Если по контексту тип не может быть определен, например:


    if ($1 == $2) ...


тип поля определяется при вводе.

Поля, которые являются нулевыми, имеют строки со значением "", они не являются цифровыми.

Определения типов для элементов массива, созданных split(), аналогичны определению типов для полей.

Так, если arr[i] не существует, то :

if (arr[i]

 == "") ...


приводит к тому, что он появляется со значением "".


эта статья с Компьютерные сети и технологии
( http://xnets.ru/plugins/content/content.php?content.142 )