163

      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 и затем  из  стандартного  файла


                            164
ввода. Если программа является большой по объему, то удобнее
использовать следующий формат:
     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}


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

     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


                            166
     Получим печать всех строк, в которых третье поле  более
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}
     Первым  действием  является  накопление   значений   из
третьего поля каждой строки;  второе  действие,  выполняемое


                            167
после  последнего  ввода,  -  напечатать  сумму  и   среднее
значение:
      Общее количество населения       2201 млн
      Среднее количество населения
            10 стран                   220.1


     10.4.2.1.   Функции

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

     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


                            168
             $3 < 200 {print >>>$1}<<<
        awk: illegal statement at source line 1
             1 extra (
     Некоторые ошибки могут быть обнаружены во время  работы
программы. Например,  если  вы  попытаетесь  поделить  на  0
(ноль), то  awk  остановит  обработку  и  распечатает  номер
записи вводного файла (NR) и номер строки в программе.


     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.   Выражения отношения



                            169
     В   качестве   шаблона   может   использоваться   любое
выражение, вызывающее сравнение между строками символов  или
цифр. 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

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

     awk обеспечивает более мощные шаблоны для поиска строки
символов,   чем   сравнение.   Такие   шаблоны    называются
регулярными выражениями.  Простейшим  регулярным  выражением
является строка символов, обрамленная знаками "/". Например:
     /Asia/
     Эта программа печатает  все  записи,  которые  содержат
подстроку Asia (запись, содержащая Asia  как  часть  длинной
строки, подобной Asian или Pan-Asiatic, также печатается).
     Чтобы ограничить поиск только для специального поля, вы
можете использовать операторы  ~(входит)  и  !~(не  входит).
Программа:
     $4 ~ /Asia/ { print $1 }
печатает первое поле всех тех  строк,  в  которых  четвертое
поле - Asia, в то время как программа:
     $4 !~ /Asia/ { print $1 }
печатает первое поле всех тех  строк,  в  которых  четвертое
поле - не Asia.
     В    регулярном    выражении    могут    использоваться
метасимволы:
     \, ^, $, ., [, ], *, ?, (, ), |


                            170
которые  подобны   метасимволам,   используемым   в   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]+$" используется как регулярное


                            171
выражение,  появляются  дополнительные  знаки  "\",  которые
защищают регулярное выражение. Это связано с тем, что первый
уровень знаков  "\"  удаляется  при  синтаксическом  анализе
строки. Первый уровень "\"  перед  символом  возвращает  его
специальное значение в регулярном выражении,  второй  нужен,
чтобы защитить его в строке.
     Например,  нужно  найти  строки,   содержащие   "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
 -----------------------------------------------------------

     10.10.   Комбинация шаблонов
     Составной  шаблон   комбирирует   простые   шаблоны   с
логическими операторами "||" (или), "&&" (и),  !(отрицание).
Например, нужно напечатать все страны в "Asia" с  населением
более 500 млн.  Следующая  программа  выполняет  выбор  всех
строк, у которых 4-е поле "Asia"  и  третье  поле  превышает
500:
     $4 == "Asia" && $3 > 500
     Программа:
     $4 == "Asia" || $4 == "Africa"
выбирает строки с названиями "Asia" или "Africa" в 4-м поле.
Эти  же  действия  можно  выполнить  с  помощью  регулярного
выражения и альтернативного оператора "|":
     $4 ~ /^(Asia|Africa)$/


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

     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.

     10.12.   Действие

     В выражении шаблон-действие, "действие" определяет  то,
что нужно выполнить с вводными записями, которые отобраны по
шаблону. Достаточно часто "действие" - это печать каких-либо
выражений, но также может быть комбинацией одного или  более
выражений.
     Встроенные переменные
     В табл. 23  приведены  встроенные  переменные,  которые
поддерживает awk.
                                                  Таблица 23
                   Встроенные переменные
 -----------------------------------------------------------
 Переменная     | Значение               | Умолчание
 -----------------------------------------------------------
   ARGC         |Число аргументов команд-|      -
                |ной строки              |
   ARGV         |Массив аргументов       |      -
                |командной строки        |
   FILENAME     |Имя текущего вводного   |      -
                |файла                   |
   FNR          |Номер записи в текущем  |      -
                |файле                   |
   FS           |Поле разделителя        |пробел и/или
                |вводного файла          |табуляция
   FN           |Число полей в текущей   |       -
                |записи                  |
   NR           |Число считанных на      |       -
                |данный момент записей   |
   OFMT         |Выводной формат для цифр|    %.6g
   OFS          |Разделитель поля вывод- |  пробел


                            173
                                        Продолжение табл. 23
 -----------------------------------------------------------
 Переменная     | Значение               | Умолчание
 -----------------------------------------------------------
                |ного файла              |
   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,


                            174
              "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() не имеет аргументов, то
начальное значение производится из времени дня.


                            175
     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 }


                            176
преобразует ввод, меняя появление "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


                            177
        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

     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.   Номер или строка

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


                            178
контексте выражения, подобного следующему:
     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
(ноль) и строковое значение "".
     Несуществующие поля имеют только строковое значение "";
они не являются цифровыми.

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

     awk поддерживает  операторы  if-else,  while,  do-while
аналогично языку программирования Си.
     Синтаксис оператора if:
     if (выражение) оператор_1 else оператор_2
"выражение" является условным и не  имеет  ограничений.  Оно
может включать операторы отношений:
     <, <=, >, >=, ==, !=
регулярные выражения:
     ~, !~
логические операторы:
     ||, &&, !
операторы слияния и круглые скобки для группирования.
     В операторе if awk сначала вычисляет "выражение".  Если
оно не ноль  и  не  пустое,  то  оператор_1  выполняется,  в


                            179
противном  случае  выполняется  оператор_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 равно нулю.

     10.17.   Массивы

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


                            180
быть  числом  или  строкой.  Пример  условного   обозначения
числового индекса:
     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).


                            181
Например:   for ( i = 1; i <= 10; i++ )
               for ( j = 1; j <= 10; j++ )
                    arr[i, j] = ...
создает массив, который ведет  себя  как  двумерный  массив.
Индексом является сочетание 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 # это комментарий
     Операторы  обычно  занимают  одну   строку.   Несколько
операторов могут располагаться на одной  строке,  тогда  они


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

     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 - это строка, содержащая информацию, которая


                            183
             будет печататься, и какое преобразование будет
             выполняться над выражением.
     Каждая   спецификация   начинается   с   символа   "%",
заканчивается буквой, которая определяет  преобразование,  и
может включать:
- - выравнивание по левому краю в поле;
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"  }


                            184
        $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  (файл)  закрывает
файл или канал.
     Когда  открывается  или  закрывается  файл,   различные
строки являются различными командами.

     10.21.   Ввод

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


                            185
     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()), но не  нулевых
строк.


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

     Обычно записи разделяются символами новой  строки,  так
что каждая строка  яавяется  записью.  Такой  порядок  можно
изменить. Если пременная RS - разделитель встроенных записей
- установлен в значение "пустая строка", как в:
     BEGIN { RS = "" }
то записи вводного файла  могут  занимать  несколько  строк.
Последовательность  пустых  строк  разделяет   записи.   Для
обработки  многострочных  записей  в  общем   случае   может
использоваться:
     BEGIN { RS = ""; FS = "\n" }
установка в качестве разделителя записи символ пустой строки
и разделителя поля - символ  новой  строки.  Таким  образом,
каждая строка является  одним  полем.  Однако  длина  записи
ограничена (обычно 2500 символов).


                            186
     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]) ) { ... }
т.  к.   операция   "<"   имеет   больший   приоритет,   чем
конкатенация. Без круглых скобок оператор подобный:


                            187
     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


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

     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,   который
содержит:


                            189
        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 '
        BEGI   { RS = ""
                 while ( getline < "addresslist" )
                   if ($0 ~ ARGV[1]
                      print $0
        } ' $*
     Вся обработка выполняется в "действии" оператора BEGIN.
     Обратите внимание, что регулярное выражение может  быть
передано  addr.  В  частности,  возможно  отыскать  отдельно
адрес, или номер телефона, или имя.

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

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

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

     awk особенно успешно применяется  для  выдачи  отчетов,
которые суммируют и форматируют информацию. Предположим,  вы
хотите  создать  отчет  из  файла   countries,   в   котором
континенты перечисляются в алфавитном порядке и  по  каждому
континету страны  перечисляются  в  убывающем  по  населению
порядку:
        Africa:
                Sudan            19
                Algeria          18
        Asia:
                China           866
                India           637
                USSR            262


                            190
        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
дает требуемый отчет. В  этом  примере  предполагается,  что
сложные задачи преобразования  данных  и  их  форматирования


                            191
могут быть сокращены до  нескольких  простых  команд  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   приблизительно   моделирует


                            192
возможности 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) }

     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 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


                            194
  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           разделитель поля вводного файла;
                (по умолчанию - пробел)


                            195
   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") ...
является ложью.


                            196
     Тип поля определяется по контексту. Например:
     $1++
означает, что $1 будет цифровым, и
     $1 = $1 "," $2
означает, что и $1 и $2 являются  строковыми.  Приведение  к
типу выполняется при необходимости.
     Если  по  контексту  тип  не  может   быть   определен,
например:
     if ($1 == $2) ...
тип поля определяется при вводе.
     Поля,  которые  являются  нулевыми,  имеют  строки   со
значением "", они не являются цифровыми.
     Определения  типов  для  элементов  массива,  созданных
split(), аналогичны определению типов для полей.
     Так, если arr[i] не существует, то :
     if (arr[i] == "") ...
приводит к тому, что он появляется со значением "".