<<< оглавление >>>

руководство пользователя для gnu awk

arnold d. robbins
перевод балуева а. н.

2. начало awk

начальные сведения об awk

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

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

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

pattern {action} pattern {action} ...

2.1 роза под любым другим именем

язык awk разрабатывался несколько лет. полностью этот процесс описан в гл. 17 [развитие языка awk], стр.253. на описанный в книге язык ссылаются часто как на "новый awk." на многих системах имеются разные версии языка. некоторые системы имеют интерпретатор awk, реализующий начальную версию языка и средства awk для новой версии. другие имеют oawk для "старого awk" и простой awk для новой версии. третьи имеют только одну версию, обычно новую.

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

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

2.2 как запускать awk-программы

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

awk 'program' input-file1 input-file2 ...

где программа состоит из последовательности образцов и действий, как говорилось выше. (причина одиночных кавычек объясняется ниже, в разделе 2.2.1) [короткие разовые awk-программы], стр. 10.

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

awk -f program-file input-file1 input-file2 ...

2.2.1 короткие разовые awk-ппрограммы

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

awk 'program' input-file1 input-file2 ...

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

гл. 3 [полезные однострочные программы], стр. 21, предлагает несколько коротких самостоятельных программ.

между прочим, команда

awk '/foo/' files ...

по существу то же самое как

egrep foo files ...

2.2.2 выполнение awk без входных файлов

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

awk 'program'

то awk обратится к программе стандартного ввода, которая обычно означает то, что вы печатаете на терминале. это продолжается, пока вы не укажете end-of-file, напечатав control-d. (в других операционных системах end-of-file может быть другим символом. например, в os/2 и ms-dos, это control-z.)

например, следующая программа печатает дружественный совет (из книги douglas adams руководство для путешествующего автостопом по галактике) воздерживаться от жалоб на сложности компьютерного программирования (со свойством `begin' мы еще не знакомы).


$ awk "begin { print \"don't panic!\" }"
-| don't panic!

эта программа не требует никакого ввода. `\' перед каждой внутренней двойной кавычкой необходимо вследствие оболочечных правил для кавычек, в частности потому, что они смешивают ординарные и двойные кавычки.

следующая простая awk-программа эмулирует средство cat; она копирует все, что вы печатаете на клавиатуре, на стандартный выход. (как это происходит, будет вскоре пояснено.)

$ awk '{ print }'
now is the time for all good men
-| now is the time for all good men 
to come to the aid of their country.
-| to come to the aid of their country. 
four score and seven years ago, ...
-| four score and seven years ago, ... 
what, me worry?
-| what, me worry? 
control-d

2.2.3 выполнение длинных программ

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

awk -f source-file input-file1 input-file2 ...

`-f' побуждает интерпретатор awk извлекать awk-программу из файла source-file. любое файловое имя может использоваться в качестве source-file. например, можно поместить программу:

begin { print "don't panic!" }

в файл  `advice'. тогда такая команда:

awk -f advice

сделает то же, что и следующая:

awk "begin { print \"don't panic!\" }"

что было объяснено ранее (см. раздел 2.2.2 [выполнение awk без входных файлов], стр. 11). заметим, что вам обычно не нужны одиночные кавычки вокруг имени файла, который вы специфицируете как `-f', потому что большинство файловых имен не содержит специальных символов оболочки. заметьте, что в `advice' awk-программа не заключена в одиночные кавычки. они нужны только для программ, которые вставляются в командную строку awk.

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

2.2.4 исполнимые awk-программы

если вы познакомились с awk, вы можете захотеть писать самостоятельные сценарии awk, используя сценарный механизм `#!'. это можно делать на многих unix systems2 (и впоследствии и на системах gnu). например, можно модернизировать файл `advice' так:

#! /bin/awk -f
begin { print "don't panic!" }

сделав этот файл исполнимым (с помощью утилиты chmod), можно просто напечатать `advice' для командного процессора, и система выполнит awk так, как и в случае `awk -f advice':


$ advice

-| don't panic!

механизм `#!' работает на системах linux, системах unix, происходящих от berkeley unix, на системах v release 4, и некоторых system v release 3. строка с началом `#!' определяет полное имя файла интерпретатора, который нужно запустить, и возможный начальный аргумент командной строки для передачи этому интерпретатору. затем операционная система запускает интерпретатор с этим аргументом и полным списком аргументов исполняемой программы. первый аргумент в списке - есть полное файловое имя awk-программы. остальные аргументы списка --- или параметры awk, или файлы с данными, или и то и другое.

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

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

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

: двоеточие обеспечивает выполнение стандартной оболочкой. awk 'program' "$@"

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

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

2.2.5 комментарии в awk-программах

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


# эта программа печатает забавное дружеское сообщение. оно помогает
# пользователям-новичкам не бояться компьютера.
begin { print "don't panic!" }

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

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

 например:

awk '{ print "hello" } # let's be cute'

2.3 очень простой пример

следующая команда выполняет простую awk-программу, которая ищет во входном файле `bbs-list' цепочки символов `foo'. (цепочка символов (string) обычно называется цепочкой. этот термин происходит от английских наименований типа "цепочка жемчужин" или "цепочка вагонов в поезде.")

awk '/foo/ { print $0 }' bbs-list.

когда находятся строки, содержащие `foo', они печатаются, потому что `print $0' означает печать текущей строки. (просто `print' означает то же самое, так что можно написать его вместо print $0.)

заметьте, что слеши `/' окружают цепочку `foo' в awk-программе. слеши указывают, что `foo' есть образец для поиска. этот тип образца называется регулярным выражением и объясняется в деталях ниже (см. гл. 4 [регулярные выражения], стр. 23). образец позволяет различать части слов. awk-программы заключаются в одиночные кавычки, так что оболочка не будет рассматривать слеши как специальные символы оболочки.

вот что напечатает наша программа:

$ awk '/foo/ { print $0 }' bbs-list

a fooey 555-1234 2400/1200/300 b a foot 555-6699 1200/300 b a macfoo 555-6480
1200/300 a a sabafoo 555-2127 1200/300 c

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

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

2.4 пример с двумя правилами

интерпретатор awk читает входные файлы построчно. к каждой строке применяются образцы всех правил. если строке соответствуют несколько образцов, соответствующие действия выполняются в порядке, в котором они фигурируют в программе awk. если входной строке не соответствует ни один образец, никакие действия не выполняются. после обработки всех правил (возможно никаких), соответствующих строке, интерпретатор читает следующую строку (однако, см. раздел 9.7 [следующий оператор], стр. 111, и также раздел 9.8 [оператор nextfile], стр. 112). это продолжается до достижения конца файла.


например, awk-программа:

/12/ { print $0 } /21/ { print $0 }

содержит два правила. образец первого правила есть цепочка `12', а действие `print $0'. второе правило имеет образец `21' и действием тоже `print $0'. каждое правило заключено в свою собственную пару скобок.

эта программа печатает каждую строку, содержащую цепочку `12' или цепочку `21'. если строка содержит обе цепочки, она печатается дважды, один раз по каждому правилу. вот что будет, если мы выполним эту программу на двух файлах с данными, `bbs-list' и `inventory-shipped':

$ awk '/12/ { print $0 }
     >      /21/ { print $0 }' bbs-list inventory-shipped
     -| aardvark     555-5553     1200/300          b
     -| alpo-net     555-3412     2400/1200/300     a
     -| barfly       555-7685     1200/300          a
     -| bites        555-1675     2400/1200/300     a
     -| core         555-2912     1200/300          c
     -| fooey        555-1234     2400/1200/300     b
     -| foot         555-6699     1200/300          b
     -| macfoo       555-6480     1200/300          a
     -| sdace        555-3430     2400/1200/300     a
     -| sabafoo      555-2127     1200/300          c
     -| sabafoo      555-2127     1200/300          c
     -| jan  21  36  64 620
     -| apr  21  70  74 514

заметьте, что строка из `bbs-list', начинающаяся с `sabafoo', печатается дважды, один раз для каждого правила.

2.5 более сложный пример

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


ls -l | awk '$6 == "nov" { sum += $5 } end { print sum }'

эта программа печатает общее количество байтов во всех файлах текущего каталога, которые в последний раз модифицировались в ноябре (любого года). (в оболочке си вам нужно напечатать двоеточие и затем обратный слеш в конце первой строки; в posix-подчиненной оболочке, такой как bourne shell или bash, gnu bourne-again shell пример можно печатать, как показано.)

часть `ls -lg' этого примера есть системная команда, которая выдает список файлов в каталоге, включающий размеры файлов и даты их последнего изменения. ее выход выглядит так:

-rw-r--r-- 1 arnold user 1933 nov 7 13:05 makefile -rw-r--r-- 1 arnold user
10809 nov 7 13:03 gawk.h -rw-r--r-- 1 arnold user 983 apr 13 12:14 gawk.tab.h
-rw-r--r-- 1 arnold user 31869 jun 15 12:20 gawk.y -rw-r--r-- 1 arnold user
22414 nov 7 13:03 gawk1.c -rw-r--r-- 1 arnold user 37455 nov 7 13:03 gawk2.
c -rw-r--r-- 1 arnold user 27511 dec 9 13:07 gawk3.c -rw-r--r-- 1 arnold user
7989 nov 7 13:03 gawk4.c

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

`$6 == "nov"' в нашей awk-программе есть выражение, которое проверяет, соответствует ли шестое поле выхода от `ls -lg' цепочке `nov'. каждый раз, когда строка имеет цепочку `nov' своим шестым полем, выполняется действие `sum += $5'. оно добавляет пятое поле (размер файла) к переменной sum. в результате, когда awk окончит чтение всех входных строк, sum будет содержать сумму всех длин файлов, строки которых соответствуют образцу. (так будет, потому что все переменные awk автоматически инициализируются нулем.)

после того, как будет обработана последняя строка выхода от ls, выполняется правило end и печатается значение sum. в нашем примере это будет 80600. использованная здесь более сложная awk-техника объясняется дальше (см. раздел 8.2 [обзор действий], стр 102). прежде чем переходить к более сложному awk-программированию, вы должны узнать, как awk обрабатывает ваш ввод и выдает ваш выход. манипулируя полями и используя операторы печати, вы можете получать очень полезные и впечатляюще выглядящие отчеты.

2.6 awk операторы над строками

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

awk '/12/ { print $0 }

/21/ { print $0 }' bbs-list inventory-shipped

однако,  gawk игнорирует переходы к новой строке после каждого из следующих
слов
, { ? : || && do else

и делает newline в каждой точке, рассматриваемой как конец оператора. (расщепление строк после `?' и `:' есть простейшее расширение gawk. упомянутые здесь `?' и `:' есть трехоперандные условные выражения, описанные в разделе 7.12 [условные выражения], стр. 92.) если вы хотите расщепить единый оператор на две строки в точке, где переход к новой строке означал бы его конец, нужно продолжить его, закончив первую строку символом обратного слеш, `\'. обратный слеш должен быть последним символом строки, чтобы быть распознанным как символ продолжения. это разрешается абсолютно везде в операторе, даже внутри цепочки или регулярного выражения.

например:

awk '/this regular expression is too long, so continue it\

on the next line/ { print $1 }'

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

внимание: продолжение с обратным слеш не работает, как описано выше, с оболочкой си. продолжение с обратным слеш работает в awk-программах в файлах и также в коротких программах при условии использования posix-подчиненной оболочки, такой как bourne shell или bash, gnu bourne-again shell. но си-оболочка (csh) ведет себя не так! там вы должны взять два обратных слеш подряд и переход к новой строке. заметим также, что при использовании си-оболочки каждая newline в вашей awk-программе должна заканчиваться обратным слешем.

 например:

% awk 'begin {\
? print \\
? "hello, world" \
? }'
-| hello, world

здесь `%' and `?' есть первичная и вторичная подсказки си-оболочки, аналогичные стандартным `$' и `?'.

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

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


например:

$ gawk 'begin { print "dont panic" # a friendly \ 
  begin rule
 }'

error gawk: cmd. line:2: begin rule error 
      gawk: cmd. line:2: ^ parse error

здесь кажется, что обратный слеш продолжает комментарий на следующую строку. однако, комбинация обратный слеш-новая строка не замечается, так как относится к комментарию. поэтому `begin' отмечается как синтаксическая ошибка.

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


/12/ { print $0 } ; /21/ { print $0 }

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

2.7 другие особенности awk

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

вдобавок, awk имеет некоторое количество встроенных функций для обеспечения общих вычислительных и строковых операций. при развитии наших представлений о языке awk мы введем большинство переменных и многие из функций. все они систематически определены в гл. 10 [встроенные переменные], стр. 115 и гл. 12 [встроенные функции], стр. 135.

2.8 когда использовать awk

вы можете удивиться, насколько awk может быть полезен для вас. используя утилиты, сложные образцы, разделители полей, арифметические операторы, критерии отбора, вы можете получить гораздо более сложный результат. язык awk очень полезен для получения отчетов о больших количествах сырых данных, таких как обобщение информации, полученной от других утилит, подобных ls. (см. раздел 2.5 [более сложный пример], стр. 15.)

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

сложные программы пишутся на awk с использованием полного настраиваемого ассемблера для восьмибитовых микропроцессоров (см. детали в appendix d [глоссарий], стр. 301) и ассемблера микрокода для prolog-компьютеров специального назначения. однако, возможности awk не всегда достаточны для задач большой сложности. если вам приходится писать сценарии awk длиной более чем, скажем, несколько сот строк, следует попробовать воспользоваться другим программным языком. например, использовать emacs lisp, если вам необходимо употреблять запутанные строки и сложные образцы. командный процессор также может помочь с цепочками и соответствием образцов; кроме того, он позволяет полностью использовать системные утилиты. более распространенные языки, такие как c, c++ и lisp, предоставляют возможности для системного программирования и преодоления сложности больших программ. программы на этих языках могут иметь больше строк входного кода, чем эквивалентные awk-программы, но их легче поддерживать и обычно работают они более эффективно.


<<< оглавление >>>