Парадигма | Скриптовый , процедурный , управляемый данными [1] |
---|---|
Разработано | Альфред Ахо , Питер Вайнбергер и Брайан Керниган |
Впервые появился | 1977 ( 1977 ) |
Стабильный релиз | Стандарт IEEE 1003.1-2008 (POSIX) / 1985 |
Дисциплина набора текста | нет; может обрабатывать строки, целые числа и числа с плавающей точкой; регулярные выражения |
ОС | Кроссплатформенный |
Основные внедрения | |
awk, GNU Awk, mawk, nawk, MKS AWK, Thompson AWK (компилятор), Awka (компилятор) | |
Диалекты | |
старый awk oawk 1977 года, новый awk nawk 1985 года, GNU Awk gawk | |
Под влиянием | |
C , sed , СНОБОЛ [2] [3] | |
Под влиянием | |
Tcl , AMPL , Perl , Korn Shell ( ksh93 , dtksh , tksh ), Lua |
AWK ( / ɔː k / [4] ) — это предметно-ориентированный язык , разработанный для обработки текста и обычно используемый в качестве инструмента извлечения данных и составления отчетов. Как и sed и grep , это фильтр , [4] и стандартная функция большинства Unix-подобных операционных систем .
Язык AWK — это язык сценариев , управляемый данными , состоящий из набора действий, которые необходимо выполнить с потоками текстовых данных — либо напрямую в файлах, либо как часть конвейера — для извлечения или преобразования текста, например, для создания форматированных отчетов. Язык широко использует строковый тип данных , ассоциативные массивы (то есть массивы, индексированные ключевыми строками) и регулярные выражения . Хотя AWK имеет ограниченную область применения и был специально разработан для поддержки однострочных программ , язык является полным по Тьюрингу , и даже ранние пользователи AWK из Bell Labs часто писали хорошо структурированные большие программы AWK. [5]
AWK был создан в Bell Labs в 1970-х годах, [6] и его название происходит от фамилий его авторов: Альфред Ахо (автор egrep ), Питер Вайнбергер (который работал над крошечными реляционными базами данных) и Брайан Керниган . Аббревиатура произносится так же, как название вида птиц auk , которое изображено на обложке The AWK Programming Language . [7] При написании строчными буквами, как awk
, она относится к программе Unix или Plan 9 , которая запускает скрипты, написанные на языке программирования AWK.
По словам Брайана Кернигана, одной из целей AWK было создание инструмента, который мог бы легко манипулировать как числами, так и строками. AWK также был вдохновлен языком программирования Марка Рохкинда , который использовался для поиска шаблонов во входных данных и был реализован с использованием yacc . [8]
Как один из первых инструментов, появившихся в версии 7 Unix , AWK добавил вычислительные возможности в конвейер Unix, помимо оболочки Bourne , единственного языка сценариев, доступного в стандартной среде Unix. Это одна из обязательных утилит Single UNIX Specification [ 9] и требуется спецификацией Linux Standard Base . [10]
В 1983 году AWK был одним из нескольких инструментов UNIX, доступных для операционной системы UNOS компании Charles River Data Systems по лицензии Bell Laboratories . [11]
AWK был значительно переработан и расширен в 1985–1988 годах, что привело к реализации GNU AWK, написанной Полом Рубином, Джеем Фенласоном и Ричардом Столлманом и выпущенной в 1988 году. [12] GNU AWK, возможно, является наиболее широко используемой версией [13], поскольку она включена в пакеты Linux на основе GNU. GNU AWK поддерживается исключительно Арнольдом Роббинсом с 1994 года. [12] Исходный код nawk (New AWK) Брайана Кернигана был впервые выпущен в 1993 году неопубликованным, а публично — с конца 1990-х; многие системы BSD используют его, чтобы обойти лицензию GPL. [12]
AWK предшествовал sed (1974). Оба были разработаны для обработки текста. Они разделяют парадигму, ориентированную на строки, управляемую данными, и особенно подходят для написания однострочных программ из-за неявного основного цикла и текущих переменных строки. Мощность и краткость ранних программ AWK — в частности, мощная обработка регулярных выражений и краткость из-за неявных переменных, которые облегчают однострочные программы — вместе с ограничениями AWK в то время были важными источниками вдохновения для языка Perl (1987). В 1990-х годах Perl стал очень популярен, конкурируя с AWK в нише языков обработки текста Unix.
AWK считывает ввод построчно. Строка сканируется для каждого шаблона в программе, и для каждого совпадающего шаблона выполняется соответствующее действие.
— Альфред В. Ахо [14]
Программа AWK представляет собой серию пар шаблонных действий, записанных следующим образом:
условие { действие } условие { действие } ...
где условие обычно является выражением, а действие — серией команд. Ввод разбивается на записи, где по умолчанию записи разделяются символами новой строки, так что ввод разбивается на строки. Программа проверяет каждую запись на соответствие каждому из условий по очереди и выполняет действие для каждого выражения, которое является истинным. Можно опустить либо условие, либо действие. По умолчанию условие соответствует каждой записи. Действие по умолчанию — печать записи. Это та же структура шаблон-действие, что и sed.
Помимо простого выражения AWK, такого как foo == 1
или /^foo/
, условием может быть BEGIN
или , END
вызывающее выполнение действия до или после прочтения всех записей, или pattern1, pattern2 , которое соответствует диапазону записей, начиная с записи, соответствующей pattern1 , и до записи, соответствующей pattern2 , включая ее , прежде чем снова попытаться сопоставить pattern1 в последующих строках.
В дополнение к обычным арифметическим и логическим операторам выражения AWK включают оператор тильды, ~
, который сопоставляет регулярное выражение со строкой. Как удобный синтаксический сахар , /regexp/ без использования оператора тильды сопоставляет с текущей записью; этот синтаксис происходит от sed , который в свою очередь унаследовал его от редактора ed , где /
используется для поиска. Этот синтаксис использования косых черт в качестве разделителей для регулярных выражений был впоследствии принят Perl и ECMAScript и теперь является общепринятым. Оператор тильды также был принят Perl.
Команды AWK — это операторы, которые заменяют действия в приведенных выше примерах. Команды AWK могут включать вызовы функций, присваивания переменных, вычисления или любые их комбинации. AWK содержит встроенную поддержку многих функций; многие другие функции предоставляются различными разновидностями AWK. Кроме того, некоторые разновидности поддерживают включение динамически подключаемых библиотек , которые также могут предоставлять больше функций.
Команда print используется для вывода текста. Выводимый текст всегда завершается предопределенной строкой, называемой разделителем выходных записей (ORS), значением по умолчанию которой является новая строка. Простейшая форма этой команды:
print
print $1
print $1, $3
Хотя эти поля ( $X ) могут иметь сходство с переменными (символ $ обозначает переменные в обычных оболочках Unix и в Perl ), на самом деле они ссылаются на поля текущей записи. Особый случай, $0 , ссылается на всю запись. Фактически, команды " print
" и " print $0
" идентичны по функциональности.
Команда print также может отображать результаты вычислений и/или вызовов функций:
/regex_pattern/ { # Действия, которые необходимо выполнить в случае, если запись (строка) соответствует указанному выше regex_pattern print 3 + 2 print foobar ( 3 ) print foobar ( variable ) print sin ( 3 - 2 ) }
Вывод может быть отправлен в файл:
/regex_pattern/ { # Действия, которые необходимо выполнить в случае, если запись (строка) соответствует указанному выше regex_pattern print "expression" > "file name" }
или через трубу :
/regex_pattern/ { # Действия, которые необходимо выполнить в случае, если запись (строка) соответствует указанному выше regex_pattern print "выражение" | "команда" }
Встроенные переменные AWK включают переменные поля: $1, $2, $3 и т. д. ($0 представляет всю запись). Они содержат текст или значения в отдельных текстовых полях записи.
Другие переменные включают в себя:
NR
: Количество записей. Ведет текущий подсчет количества входных записей, считанных на данный момент из всех файлов данных. Начинается с нуля, но никогда автоматически не сбрасывается на ноль. [15]FNR
: Количество записей в файле. Сохраняет текущий счетчик количества прочитанных на данный момент входных записей в текущем файле. Эта переменная автоматически сбрасывается на ноль каждый раз при запуске нового файла. [15]NF
: Количество полей. Содержит количество полей в текущей входной записи. Последнее поле во входной записи может быть обозначено как $NF, 2-е с конца поле как $(NF-1), 3-е с конца поле как $(NF-2) и т. д.FILENAME
: Содержит имя текущего входного файла.FS
: Разделитель полей. Содержит «разделитель полей», используемый для разделения полей во входной записи. Значение по умолчанию «пробел» допускает любую последовательность символов пробела и табуляции. FS можно переназначить с другим символом или последовательностью символов для изменения разделителя полей.RS
: Разделитель записей. Сохраняет текущий символ "разделитель записей". Поскольку по умолчанию строка ввода является записью ввода, символом разделителя записей по умолчанию является "новая строка".OFS
: Разделитель выходных полей. Сохраняет "разделитель выходных полей", который разделяет поля, когда awk их печатает. По умолчанию используется символ "пробел".ORS
: Разделитель выходных записей. Сохраняет "разделитель выходных записей", который разделяет выходные записи, когда awk их печатает. По умолчанию используется символ "новая строка".OFMT
: Формат вывода. Сохраняет формат числового вывода. Формат по умолчанию — "%.6g".Имена переменных могут использовать любые символы [A-Za-z0-9_], за исключением ключевых слов языка, и не могут начинаться с цифры. Операторы + - * / представляют сложение, вычитание, умножение и деление соответственно. Для конкатенации строк просто поместите две переменные (или строковые константы) рядом друг с другом. Необязательно использовать пробел между ними, если задействованы строковые константы, но два имени переменных, размещенные рядом друг с другом, требуют пробела между ними. Двойные кавычки разделяют строковые константы. Операторы не обязательно заканчиваются точкой с запятой. Наконец, комментарии можно добавлять в программы, используя # в качестве первого символа в строке или после команды или последовательности команд.
В формате, похожем на C , определения функций состоят из ключевого слова function
, имени функции, имен аргументов и тела функции. Вот пример функции.
функция add_three ( число ) { возврат числа + 3 }
Это утверждение можно сформулировать следующим образом:
( шаблон ) { print add_three ( 36 ) # Выводит '''39''' }
Функции могут иметь переменные, которые находятся в локальной области видимости. Их имена добавляются в конец списка аргументов, хотя значения для них следует опускать при вызове функции. Принято добавлять пробелы в список аргументов перед локальными переменными, чтобы указать, где заканчиваются параметры и начинаются локальные переменные.
Вот обычная программа «Hello, World!», написанная на AWK:
НАЧАТЬ { print "Привет, мир!" выход }
Печатать все строки длиннее 80 символов. Действие по умолчанию — печатать текущую строку.
длина ( $ 0 ) > 80
Подсчитайте количество слов во входных данных и выведите количество строк, слов и символов (например, wc ):
{ words += NF chars += length + 1 # добавьте единицу для учета символа новой строки в конце каждой записи (строки) } END { print NR , words , chars }
Поскольку для первой строки программы шаблона нет, по умолчанию сопоставляется каждая строка ввода, поэтому действия по увеличению выполняются для каждой строки. words += NF
— это сокращение от words = words + NF
.
{ s += $ NF } END { print s + 0 }
s увеличивается на числовое значение $NF , которое является последним словом в строке, как определено разделителем полей AWK (по умолчанию, пробелом). NF — это количество полей в текущей строке, например, 4. Поскольку $4 — это значение четвертого поля, $NF — это значение последнего поля в строке, независимо от того, сколько полей в этой строке или больше или меньше полей в ней, чем в соседних строках. $ — это на самом деле унарный оператор с наивысшим приоритетом оператора . (Если в строке нет полей, то NF равно 0, $0 — это вся строка, которая в этом случае пуста, за исключением возможных пробелов, и поэтому имеет числовое значение 0.)
В конце ввода совпадает шаблон END , поэтому печатается s . Однако, поскольку строк ввода могло не быть вообще, и в этом случае s не было присвоено никакого значения , по умолчанию это будет пустая строка. Добавление нуля к переменной — это идиома AWK для приведения ее из строки в числовое значение. (Конкатенация пустой строки — это приведение из числа в строку, например s "" . Обратите внимание, что оператора для конкатенации строк нет, они просто размещаются рядом.) С приведением программа печатает "0" на пустом вводе, без него печатается пустая строка.
NR % 4 == 1 , NR % 4 == 3 { printf "%6d %s\n" , NR , $ 0 }
Оператор действия выводит каждую пронумерованную строку. Функция printf эмулирует стандартную функцию printf языка C и работает аналогично команде print, описанной выше. Однако шаблон для сопоставления работает следующим образом: NR — это количество записей, обычно строк ввода, которые AWK уже прочитал, т. е. текущий номер строки, начиная с 1 для первой строки ввода. % — оператор деления по модулю . NR % 4 == 1 истинно для 1-й, 5-й, 9-й и т. д. строк ввода. Аналогично, NR % 4 == 3 истинно для 3-й, 7-й, 11-й и т. д. строк ввода. Шаблон диапазона является ложным, пока не совпадет первая часть в строке 1, а затем остается истинным вплоть до совпадения второй части в строке 3. Затем он остается ложным, пока первая часть снова не совпадет в строке 5.
Таким образом, программа печатает строки 1,2,3, пропускает строку 4, затем 5,6,7 и т. д. Для каждой строки она печатает номер строки (в поле шириной 6 символов), а затем содержимое строки. Например, при выполнении на этом входе:
РимФлоренцияМиланНеапольТуринВенеция
Предыдущая программа печатает:
1 Рим 2 Флоренция 3 Милан 5 Турин 6 Венеция
В качестве особого случая, когда первая часть шаблона диапазона постоянно истинна, например 1 , диапазон начнется с начала ввода. Аналогично, если вторая часть постоянно ложна, например 0 , диапазон будет продолжаться до конца ввода. Например,
/^--вырезать здесь--$/ , 0
выводит строки ввода, начиная с первой строки, соответствующей регулярному выражению ^--cut here--$ , то есть строки, содержащей только фразу "--cut here--", до конца.
Частота слов с использованием ассоциативных массивов :
BEGIN { FS = "[^a-zA-Z]+" } { for ( i = 1 ; i <= NF ; i ++ ) words [ tolower ( $ i )] ++ } END { for ( i in words ) print i , words [ i ] }
Блок BEGIN устанавливает разделитель полей на любую последовательность неалфавитных символов. Разделителями могут быть регулярные выражения. После этого мы получаем голое действие, которое выполняет действие на каждой входной строке. В этом случае для каждого поля в строке мы добавляем единицу к количеству раз, когда появляется это слово, сначала преобразованное в нижний регистр. Наконец, в блоке END мы печатаем слова с их частотами. Строка
для (i в словах)
создает цикл, который проходит по массиву words , устанавливая i для каждого индекса массива. Это отличается от большинства языков, где такой цикл проходит по каждому значению в массиве. Таким образом, цикл выводит каждое слово, за которым следует его частота. tolower
был дополнением к One True awk (см. ниже), сделанным после публикации книги.
Эту программу можно представить несколькими способами. Первый из них использует оболочку Bourne для создания скрипта оболочки, который делает все. Это самый короткий из этих методов:
#!/bin/шшаблон = " $1 " shift
awk '/' " $pattern " '/ { print FILENAME ":" $0 }' " $@ "
В $pattern
команде awk не защищен одинарными кавычками, поэтому оболочка расширяет переменную, но ее необходимо поместить в двойные кавычки, чтобы правильно обрабатывать шаблоны, содержащие пробелы. Шаблон сам по себе обычным образом проверяет, $0
совпадает ли вся строка ( ). FILENAME
содержит текущее имя файла. В awk нет явного оператора конкатенации; две соседние строки объединяют их. $0
расширяется до исходной неизмененной входной строки.
Есть альтернативные способы записи этого. Этот скрипт оболочки обращается к среде напрямую из awk:
#!/bin/шэкспорт шаблон = " $1 " shift
awk '$0 ~ ENVIRON["pattern"] { print FILENAME ":" $0 }' " $@ "
Это скрипт оболочки, который использует ENVIRON
, массив, введенный в новой версии One True awk после публикации книги. Нижний индекс ENVIRON
— это имя переменной окружения; его результат — значение переменной. Это похоже на функцию getenv в различных стандартных библиотеках и POSIX . Скрипт оболочки создает переменную окружения, pattern
содержащую первый аргумент, затем удаляет этот аргумент и заставляет awk искать шаблон в каждом файле.
~
проверяет, соответствует ли его левый операнд его правому операнду; !~
является его инверсией. Регулярное выражение — это просто строка, и ее можно хранить в переменных.
Следующий способ использует присваивание переменной в командной строке, в котором аргумент awk можно рассматривать как присваивание переменной:
#!/bin/шшаблон = " $1 " shift
awk '$0 ~ шаблон { print FILENAME ":" $0 }' шаблон = " $pattern " " $@ "
Или вы можете использовать параметр командной строки -v var=value (например, awk -v pattern="$pattern" ... ).
Наконец, это написано на чистом awk, без помощи оболочки или без необходимости слишком много знать о реализации скрипта awk (как это необходимо при назначении переменной в командной строке), но это немного длинно:
BEGIN { pattern = ARGV [ 1 ] for ( i = 1 ; i < ARGC ; i ++ ) # удалить первый аргумент ARGV [ i ] = ARGV [ i + 1 ] ARGC -- if ( ARGC == 1 ) { # шаблон был единственным, поэтому принудительно считываем из стандартного ввода (используется книгой) ARGC = 2 ARGV [ 1 ] = "-" } } $ 0 ~ pattern { print FILENAME ":" $ 0 }
Необходимо BEGIN
не только для извлечения первого аргумента, но и для предотвращения его интерпретации как имени файла после BEGIN
окончания блока. ARGC
, количество аргументов, всегда гарантированно будет ≥1, как и ARGV[0]
имя команды, выполнившей скрипт, чаще всего строка "awk"
. ARGV[ARGC]
— пустая строка, ""
. #
инициирует комментарий, который расширяется до конца строки.
Обратите внимание на if
блок. awk проверяет только, следует ли ему читать из стандартного ввода, прежде чем он выполнит команду. Это означает, что
awk 'прог'
работает только потому, что факт отсутствия имен файлов проверяется только перед prog
запуском! Если вы явно установите ARGC
значение 1, чтобы не было аргументов, awk просто завершит работу, поскольку посчитает, что больше нет входных файлов. Поэтому вам нужно явно указать, что нужно читать из стандартного ввода со специальным именем файла -
.
В операционных системах типа Unix автономные скрипты AWK могут быть созданы с использованием синтаксиса shebang .
Например, скрипт, отправляющий содержимое заданного файла на стандартный вывод, можно создать, создав файл print.awk
со следующим содержимым:
#!/usr/bin/awk -f { print $ 0 }
Его можно вызвать с помощью:./print.awk <filename>
Сообщает -f
awk, что следующий аргумент — это файл, из которого следует прочитать программу AWK, что является тем же флагом, который используется в sed. Поскольку они часто используются для однострочных команд, обе эти программы по умолчанию выполняют программу, заданную в качестве аргумента командной строки, а не отдельного файла.
Первоначально AWK был написан в 1977 году и распространялся вместе с версией 7 Unix .
В 1985 году его авторы начали расширять язык, наиболее существенно добавляя определяемые пользователем функции. Язык описан в книге The AWK Programming Language , опубликованной в 1988 году, а его реализация была доступна в выпусках UNIX System V. Чтобы избежать путаницы с несовместимой старой версией, эту версию иногда называли «новый awk» или nawk . Эта реализация была выпущена под лицензией свободного ПО в 1996 году и до сих пор поддерживается Брайаном Керниганом (см. внешние ссылки ниже). [ необходима цитата ]
Старые версии Unix, такие как UNIX/32V , включали awkcc
, которая преобразовывала AWK в C. Керниган написал программу для преобразования awk в C++; ее состояние неизвестно. [16]
tolower
и , ENVIRON
которые описаны выше; подробности см. в файле FIXES в исходном архиве. Эта версия используется, например, Android , FreeBSD , NetBSD , OpenBSD , macOS и illumos . Брайан Керниган и Арнольд Роббинс являются основными участниками исходного репозитория для nawk : github.com/onetrueawk/awk .В руководстве gawk есть список дополнительных реализаций AWK. [24]
[AWK] часто называют языком, управляемым данными — операторы программы описывают входные данные для сопоставления и обработки, а не последовательность шагов программы.
действий awk является полным по Тьюрингу и может читать и записывать файлы.