From: Ян Безус <xunnu at rol.ru>
Newsgroups: email
Date: Sun, 31 Mar 2006 17:02:14 +0000 (UTC)
Subject: Введение в awk
By Jos Nazario, Wed, 2006-03-08 02:00.
Перевод: Ян Безус, xunnu at rol.ru
Аннотация:
Данный документ - это краткий обзор возможностей и особенностей awk
(манипулирование файлами данных, выборка и обработка текста).
На первый взгляд, awk - это лишь упрощенный Perl. На самом деле, awk
используется даже чаще Perl'а. Его можно быстро выучить, и он может
использоваться почти повсюду в системах, на которых критична
эффективность. Этот документ нацелен на то, чтобы помочь Вам начать в
программировании на awk.
Основы
awk - небольшой C-подобный язык, cпроектированный для обработки
текстовых файлов. Это обычно дампы баз данных и системные журналы
(log-файлы). Подобно языку Perl, он построен на регулярных выражениях
и средствах обработки шаблонов. Фактически, awk является прародителем
Perl.
Забавное название awk (англ. awkward - неуклюжий) образовано из первых
букв фамилий его создателей, Alfred V. Aho, Brian W. Kernighan и Peter
J. Weinberger. Большинство из Вас, вероятно, отдадут дань уважения
Kernighan'у; он - один из отцов языка программирования 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
#
# проверка sulog на ошибки
# copyright 2001 (c) jose nazario
#
# работает на Solaris, IRIX и HPUX 10.20
BEGIN {
print "--- проверка sulog"
failed=0
}
{
if ($4 == "-") {
print "отказано в доступе:t"$6"tвt"$2"t"$3
failed=failed+1
}
}
END {
print "---------------------------------------"
printf ("tвсего записей:t%dn", NR)
printf ("tвсего отказов в доступе:t%dn", failed)
}
Вы уже поняли? Если нет, Вам может помочь формат файла sulog:
SU 01/30 13:15 - ttyq1 jose-root
SU 01/30 13:15 + ttyq1 jose-root
Теперь снова прочтем наш скрипт. Итак, блок BEGIN выводит на экран
заголовок и задает значение переменной - в нашем случае failed - в
нуль. Затем, основной цикл считывает каждую строку файла sulog, лог
попыток su - и сравнивает четвертое поле со знаком "минус". Если они
равны, это означает, что произошла неудачная попытка входа под root,
таким образом мы увеличиваем счетчик на один и смотрим, когда
пользователь пытался присвоить себе права su. И, наконец, последние
числа показывают общее количество строк файла sulog -- NR, внутренняя
переменная awk - и число неудавшихся попыток su. Скрипт выведет на
экран примерно следующее:
отказано в доступе: jose-root в 01/30 13:15
---------------------------------------
всего записей: 272
всего отказов в доступе: 73
Вы заметили, как здесь работает printf? Верно, почти так же, как и в
C. Что ни говори, awk - интуитивный язык.
По умолчанию, в качестве разделителя используется пробел, но Вы и это
можете настроить. Например, я указал двоеточие для файлов паролей.
Следующий скрипт ищет пользователей с логином 0 (эквивалентный root)
без пароля:
#!/usr/bin/awk -f
BEGIN {FS=":" }
{
if ($3 == 0) print $1
if ($2 == "") print $1
}
Другие опции awk, которые Вы должны знать и использовать, - "RS",
Record Separator (разделитель записей; по умолчанию - новая строка, n);
"OFS", Output Field Separator (разделитель полей вывода, по умолчанию
- ничего); и "ORS", Output Record Separator (разделитель записей
вывода, по умолчанию - новая строка). Конечно же, все это можно
установить и в скрипте.
Регулярные выражения
Язык awk сравнивает столь любимые Вами регулярные выражения, а это
преимущество по сравнению с grep. Например, я использую следующий
образец поиска awk для поиска уязвимостей Linux на Intel-системах:
#!/usr/bin/awk -f
{ if ($0 ~ /x90/) print "уязвимость в строке " NR }
Вы не можете использовать grep для поиска шестнадцатеричных значений
0x90, но 0x90 распространены в среде Intel. Это вызов команды НОП (не
выполняет никаких действий, управление передается следующей за ней
команде), которая используется как дополнение частей кода оболочки.
Тем не менее, Вы можете использовать awk для поиска шестнадцатеричных
значений при помощи xdd, где dd - искомое шестнадцатеричное число.
Используя ddd, Вы также можете искать десятичные (ASCII) числа и
регулярные выражения.
Генератор случайных чисел
Случайные числа в awk генерируются, но здесь есть свои нюансы. Функция
rand() делает то, что Вы и ожидаете - возвращает случайное число, в
этом случае, между 0 и 1. Конечно, Вы можете изменить интервал, чтобы
получить больше значений. Вот - пример кода, написанного чтобы
показать Вам поведение rand():
#!/usr/bin/awk -f
{
for (i=1;i<=10;i++)
print rand(); exit
}
Выполните этот код пару раз, и Вы скоро увидите проблему: случайные
числа едва случайны - они повторяются каждый раз, когда Вы выполняете
код!
В чем проблема? Хорошо, мы не запустили случайный генератор чисел. Мы
привыкли к случайному генератору чисел, перемещающему энтропию из
хорошего источника, типа /dev/random. Однако, awk не делает этого.
Чтобы получить действительно случайные числа, мы должны запустить наш
генератор случайных чисел. Код, представленный ниже делает это:
#!/usr/bin/awk -f
BEGIN {
srand()
}
{
for (i=1;i<=10;i++)
print rand(); exit
}
Запускать генератор случайных чисел нужно в блоке BEGIN. Вы можете
задать параметры для функции srand(); если их нет, используются
текущие дата и время. Заметьте, одинаковое начальное число всегда
производит ту же самую "случайную" последовательность.
Заключение
Это не самое детализированное пособие по awk, но я надеюсь, что сейчас
Вам понятно, как использовать awk. Мне лично нравится программировать
в awk, но мне нужно многому научиться. И мы даже не затронули массивы,
самостроящиеся функции и другие особенности языка. Достаточно сказать,
что awk вряд ли можно назвать младшим братом Perl'а.
Источники
Домашняя страница Kernighan'а содержит список хороших книг по awk и
nawk, а также полно других интересных ссылок и информации.
nawk, реализация awk (новый awk, противопоставляемый старому),
который иногда называют "oawk", основан на POSIX awk definitions. Он
содержит несколько функций других awk-реализаций, gawk и mawk. У меня
обычно всегда под рукой nawk для проверки мобильности моих сценариев
awk. В отличие от gawk, nawk обычно поставляется на коммерческих UNIX.
gawk (GNU project's awk), также является основанным на POSIX awk
standard, но обладает существенным количеством полезных особенностей:
особенности командной строки, типа контроля стиля программирования на
соответствие стандартам и возвращения к struct POSIX mode. Моя любимая
особенность в gawk - разрывы строк символом '' и расширенные
регулярные выражения. Документация gawk содержит полный обзор GNU
extensions для awk. gawk - стандартная версия awk для Linux и BSD.
sed и awk - возможно, самая популярная книга об этих двух маленьких
языках. Кроме всего прочего, она описывает реализации awk - gawk,
nawk, mawk - большой выбор функций и обычная для O'Reilly
удобочитаемость. Сайт по awk приводит список и других книг по awk, но
эта остается моей любимой.
Copyright (c) 2001, Jose Nazario. Originally published in Linux
Gazette issue 67. Copyright (c) 2001, Specialized Systems Consultants, Inc.