Введение в awk
Автор: © Хосе Насарио [Jose Nazario]
|
АннотацияИз-за более богатого возможностями Perl часто не обращают внимания на язык программирования awk. А между прочим, awk можно найти даже на большем числе платформ, чем Perl, его легче изучить, чем Perl и он может использоваться во всех скриптах проверки состояния системы, когда эффективность имеет ключевое значение. Это краткое руководство написано для того, чтобы помочь приступить к написанию программ на awk.
ОсновыAwk -- это компактный С-подобный язык, который был создан для обработки форматированного текста. Дампы баз данных и системные логи являются обычным примером. Awk весь построен на работе с регулярными выражениями и образцами, так же, как и Perl. Сказать правду, Perl -- это "правнук" awk. Забавное имя awk (англ. awkward -- неуклюжий) происходит от имен авторов языка: Алфреда В. Ахо [Alfred V. Aho], Брайан У. Керниган [Brian W. Kernighan] и Питер Дж. Вейнбергер [Peter J. Weinberger]. Большинство из вас сразу обратили внимание на имя Кернигана, одного из "отцов" языка программирования C и влиятельнейших фигур в мире UNIX.
Использование awk для построчной обработкиИменно так я и начал использовать awk, для вывода определенных полей. Это на удивление хорошо работало, но эффективность упала до нижней отметки, когда я начал писать большие скрипты, котором требовалось для завершения несколько минут. Но, будьте спокойны, иногда это может оказаться полезным:
ls -l /tmp/foobar | awk '{print $1"\t"$9}' Это будет получать на входе что-то наподобие: -rw-rw-rw- 1 root root 1 Jul 14 1997 tmpmsg а на выходе будет такое: -rw-rw-rw- tmpmsg То, что сделал awk, интуитивно очевидно -- он напечатал первое и девятое поле. Теперь вы понимаете, почему он так популярен для построчного извлечения данных. Но давайте перейдем к полностью "оперившейся" awk-программе.
Структура программы на awkЧто я очень люблю в awk, так это удивительную читаемость кода, несмотря на сравнимые с Perl или Python возможности. Каждая awk-программа состоит из трех частей: блока BEGIN, который выполняется один раз перед чтением входных данных; гланого цикла, который выполняется при чтении каждой входной строки и блока END, который выполняется после того, как чтение входных данных закончится. Предельно ясно! Да, я непрестанно говорю это об awk, потому что это так и есть. Вот очень простая программа, подчеркивающая некоторые особенности языка. Попробуйте сами во всем разобраться перед тем, как мы ее "препарируем": #!/usr/bin/awk -f # # проверка лога su sulog... # copyright 2001 (c) jose nazario # # работает на Solaris, IRIX и HPUX 10.20 BEGIN { print "--- проверка sulog" failed=0 } { if ($4 == "-") { print "отказано в выполнении su:\t"$6"\t дата:\t"$2"\t"$3 failed=failed+1 } } END { print "---------------------------------------" printf("\tвсего записей:\t%d\n", NR) printf("\tвсего отказов в выполнении su:\t%d\n",failed) } Еще не разобрались? Может вам поможет знание о формате строки входного файла (скажем, sulog'а от IRIX)? Вот пара типичных строк: SU 01/30 13:15 - ttyq1 jose-root SU 01/30 13:15 + ttyq1 jose-root OK, вчитайтесь и попробуйте разобраться, как работает скрипт. Блок BEGIN делает начальные установки, выводит заголовок и инициализирует одну переменную (в данном случае счетчик failed) нулем. Затем главный цикл читает каждую входную строку (а именно sulog, лог-файла попыток выполнения su) и проверяет четвертое поле на наличие знака "-". Если он там есть, то в выполнении su было отказано: мы увеличиваем счетчик и берем на заметку, какая попытка выполнить su закончилась неудачей и когда. В конце "подбиваются бабки" и выводится число строк (как число записей NR -- внутренней переменной awk) и число обнаруженных неудачных попыток выполнить su. Вывод выглядит следующим образом: failed su: jose-root at 01/30 13:15 --------------------------------------- total number of records: 272 total number of failed su's: 73 Вы также увидели, как работает printf, практически также, как и в C. Короче, awk -- довольно понятный язык. По умолчанию поля отделяются друг от друга пробелами, но это можно "подкрутить". В файлах паролей разделителем служит двоеточие. Вот маленький скрипт, который ищет пользователей с ID равным 0 (приравненным к root) и пользователей с "пустым" паролем: #!/usr/bin/awk -f BEGIN { FS=":" } { if ($3 == 0) print $1 if ($2 == "") print $1 } Другими внутренними переменными awk являются RS [record separator] для разделения записей (по умолчанию -- символ новой строки), разделитель выходных полей OFS [output field separator] (думаю, что по умолчанию он пустой) и разделитель выходных записей ORS [output record separator], по умолчанию равный символу новой строки. Естественно. что все эти переменные можно устанавливать в самом скрипте.
Регулярные выраженияЯзык awk выполняет проверку обычных регулярных выражений, и делает это лучше grep. Например, я использую следующий образец поиска для проверки на наличие вероятного эксплойта на системах Intel Linux: #!/usr/bin/awk -f { if ($0 ~ /\x90/) print "exploit at line " NR } С помощью grep нельзя осуществлять поиск шестнадцатеричного значения 0x90, но 0x90 очень "популярно" в эксплойтах на платформе Intel, т.к. это код NOP, который часто служит "заполнителем" для "забивки" кода shell. Вы можете искать шестнадцатеричные занчения с помощью \xdd, где dd обозначает шестнадцатиричное число, которое надо найти; можно искать и десятеричные значения (например коды ASCII), указывая в образце для поиска \ddd, используя десятичные значения. Текстовые регулярные выражения тоже работают, естественно.
"Случайности" в awkВ коде на awk можно генерировать случайные числа, но есть одна тонкость, которую надо иметь в виду. Функция rand() делает именно то, что вы от нее ждете, а именно возвращает случайное число, в данном случае между 0 и 1. Для получения больших значений вы можете его масштабировать. Вот пример кода, который демонстрирует использование генератора случайных числе и, заодно, показывает некоторые "интересности" поведения: #!/usr/bin/awk -f { for(i=1;i<=10;i++) print rand(); exit } Запустите этот скрипт пару раз, и проблема станет очевидна: случайные числа не так уж и "случайны" и повторяются при каждом прогоне программы! В чем проблема? Мы не дали "затравку" [seed] для генератора случайных чисел. Обычно как "элемент случайности" используется такой хороший источник, как /dev/random (в Linux). Однако, awk этого не делает. Для получения действительно случайных чисел, надо задать генератору случайных чисел "затравку" [seed]. Исправленный код выглядит так: #!/usr/bin/awk -f BEGIN { srand() } { for(i=1;i<=10;i++) print rand(); exit } Весь фокус в "запитывании" [seeding] генератора случайных чисел в блоке BEGIN. Функция srand() может получать один аргумент, а при отсутствии такового в качестве "затравки" берутся текущие время и дата. Обратите внимание, что одинаковая "затравка" всегда порождает одну и ту же "случайную" последовательность.
ЗаключениеЭто не самое подробное введение в awk из тех, которые можно найти, но я надеюсь, что то, как можно использовать этот язык, стало более понятным. Что касается меня, то мне нравится програмировать на awk, и мне еще есть, куда расти. Мы даже не коснулись массивов, самостроящихся функций и других сложных средств языка, но достаточно, чтобы сказать, что awk вряд ли можно назвать "младшим братишкой" Perl. Вперед и с песней! МатериалыНа домашней странице Кернигана находится список хороших книг по awk и исходные тексты для "настоящего awk", он же "nawk". Там же можно обнаружить кучу интересных ссылок и пояснения лично от Кернигана. http://cm.bell-labs.com/who/bwk/ Стандартная реализация awk, nawk (от new awk, в противоположность "старому awk", который иногда для совместимости называют 'oawk') основана на определениях awk в POSIX и содержит несколько функций из двух других реализаций: gawk и mawk. Я обычно держу эту реализацию под рукой под именем nawk и использую для проверки переносимости моих скриптов. С ним же я обычно сталкиваюсь на коммерческом UNIX, где часто не устанавливается gawk. Исходные тексты nawk: http://cm.bell-labs.com/who/bwk/awk.tar.gz Awk из проекта GNU, gawk, тоже базируется на POSIX стандарте awk, но добавляет значительное число полезных средств. Это включает параметры командной строки вроде провеки через 'lint' и восстановления режима строгой совместимостью с POSIX. Моими любимыми чертами gawk являются разрыв строки с помощью '\' и расширенные регулярные выражения. Подробное обсуждение расширений GNU содержится в документации. Именно gawk используется в качестве стандартного 'awk' в Linux и BSD. Исходные тексты gawk: ftp://gnudist.gnu.org/gnu/gawk/gawk-3.0.6.tar.gz (версия awk в GNU Project) А вот, наверное, самая популярная книга об этих двух маленьких программах, она котируется очень высоко. Помимо всего остального, в ней есть подробное обсуждение популярных реализаций awk (gawk, nawk, mawk и т.д.), прекрасная подборка функций, и оно прекрасно читается, как всегда у книг O'Reilly. На домашней странице awk есть список других книг по awk, но это -- моя любимая. Книга по sed & awk: http://www.oreilly.com/catalog/sed2
|
Copyright © 2001, Jose Nazario. Copying license http://www.linuxgazette.com/copying.html Published in Issue 67 of Linux Gazette, June 2001 |
Вернуться на главную страницу |