Отправляет email-рассылки с помощью сервиса Sendsay

Программирование на shell и awk

  Все выпуски  

Про awk


Информационный Канал Subscribe.Ru

Выпуск #5

Здравствуйте все!
За последние дни число подписчиков сильно возросло. Когда я увидел, что оно перевалило за полтыщи, меня охватило непонятное чувство. Эдакое волнение, которое можна охарактеризовать как «груз ответственности». Признаюсь, я не ожидал такого интереса к рассылке. Вот так сразу — без разбега.

Про awk

Начнем с примера. Если в выводе комады ls -l /bin:
total 7132
-rwxr-xr-x    1 root     root         4330 Aug 30 18:00 arch
-rwxr-xr-x    1 root     root       110048 Jul 17  2002 ash
-rwxr-xr-x    1 root     root       505685 Jul 17  2002 ash.static
-rwxr-xr-x    1 root     root        10296 Aug  4  2002 aumix-minimal
lrwxrwxrwx    1 root     root            4 Feb 11 09:41 awk -> gawk
-rwxr-xr-x    1 root     root        10680 Aug 29 18:56 basename
-rwxr-xr-x    1 root     root       626188 Aug 23 18:01 bash
lrwxrwxrwx    1 root     root            4 Feb 11 09:40 bash2 -> bash
lrwxrwxrwx    1 root     root            3 Feb 11 09:41 bsh -> ash
-rwxr-xr-x    1 root     root        19154 Jul  1  2002 cat
-rwxr-xr-x    1 root     root        18136 Sep  2 09:21 chgrp
-rwxr-xr-x    1 root     root        18072 Sep  2 09:21 chmod
-rwxr-xr-x    1 root     root        20120 Sep  2 09:21 chown
 ...                                                           Пример 5.1
вас интересуют только имя файла и его размер, то избавиться от ненужной информации несложно:

$ ls -l /bin|awk '{print $9,$5}'

arch 4330
ash 110048
ash.static 505685
aumix-minimal 10296
awk 4
basename 10680
bash 626188
bash2 4
bsh 3
cat 19154
chgrp 18136
chmod 18072
chown 20120
 ...                                                           Пример 5.2
Обратите внимание, как просто организовывается вывод информации в нужном для нас порядке.

В данном примере программа действий для awk задана как аргумент. Если же эту программу (а она находится внутри апострофов) поместить в файл, например prog, то тогда вызов awk должен быть таким:

awk -f prog

Давайте выясним, что представляет собой программа для awk. Состоит она из конструкций вида

[условие] {действие}

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

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

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

Что касается действий, то они определяются при помощи операторов и функций. Операторов есть полный джентельменский (это если для программиста) набор. В него входят операторы циклов for и while (с пред- и постусловием), условный оператор if и даже оператор присваивания. И все, к тому же, с весьма привычным для «насильников» синтаксисом. Функций есть достаточное количество встроенных (строковые, математические, файловые) и можно определять свои. Также надо отметить, что переменные в awk нетипизированные, а в добавок к ним имеются массивы, да еще и ассоциативные!

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

$ ps -e|awk '{s[$4]++};END {for (i in s) print s[i],i}'

1 syslogd
1 [kswapd]
1 drwebd
1 klogd
1 [mdrecoveryd]
1 gdm
4 login
1 in.telnetd:
6 mingetty
1 bee
1 [bdflush]
1 nmbd
1 xinetd
1 routed
1 smbd
1 [khubd]
1 init
1 ps
6 mgetty
1 bash
1 atd
1 crond
1 portmap
1 awk
3 -uucico
10 drweb
1 [kreclaimd]
1 [ksoftirqd_CPU0]
14 httpd
1 [ksoftirqd_CPU1]
1 [keventd]
1 su
1 -bash
12 sendmail:
8 uucico
1 [kupdated]
1 CMD                                                         Пример 5.3
Очевидно, что лидерами по числу запущенных копий у нас являются web-сервер httpd (14 процессов) и почтовый транспортный агент sendmail (12 процессов). На третьем месте оказался антивирус drweb в 10-ти экземплярах.

Что-то непонятно? Тогда оставайтесь с нами!


Примечания.
  1. Для юниксов семейства BSD команда должна быть такой:
    ps ax|awk '{s[$5]++};END {for (i in s) print s[i],i}'
  2. Приведенные выше примеры содержат видимые невооруженным глазом шероховатости. Их пришлось оставить, т. к. шлифовка сильно бы повлияла на простоту изложения материала. В худшую сторону конечно.

Давайте теперь более детально рассмотрим процесс обработки текста утилитой awk.

Как уже говорилось, awk читает входной поток по одной записи (построчно). Перед тем как применить к записи программу (конструкции вида [условие] {действие}), awk производит разбиение записи на поля и устанавливает значения переменных NF (количество полей в текущей записи), NR (количество прочитанных записей) и некоторых других. Затем awk выполняет действия, предусмотренные программой. После этого он считывает следующую строку и процедура, описанная в этом абзаце, повторяется снова и снова до исчерпания всех записей.

По умолчанию запись представляет собой последовательность символов, завершающуюся символом «новой строки» (в DOS/Windows признаком конца строки может быть комбинация из двух символов: «новой строки» и «перевода каретки»). Поле представляет собой последовательность символов, отличных от пробела и табуляции. Т. е. символы пробела и табуляции являются разделителями полей по умолчанию. При этом несколько символов пробела и/или табуляции, идущих подряд, считаются за один разделитель. При разбиении записи на поля ведущие пробелы и/или табуляции игнорируются:

$ echo "    aa zz" | awk '{print $1}'

aa                                                            Пример 5.4

Обращаю ваше внимание на то, что обозначения $1, $2 и т. д. не имеют никакого отношения к позиционным переменным (см. выпуск #4). В awk константа или выражение вида $<номер_поля> служит для обращения к значению поля с указанным номером.

Если в shell для получения значения какой-либо переменной необходимо написать $<имя_переменной>, то в awk нужно просто написать ее имя.Эти различия довольно часто приводят к внесению ошибок в программу. Так что будьте внимательны. Для самоконтроля привожу еще один примерчик:

$ echo aa bb cc xx yy zz | awk '{print NF, $NF}'

6 zz                                                          Пример 5.5

Вот и пришло время прощаться. До встречи на следующей неделе!


P.S.
Скачать awk для Windows можно с http://cm.bell-labs.com/cm/cs/who/bwk/awk95.exe.
Тем, кому нетерпиться узнать побольше и побыстрее, рекомендую русский перевод man-страницы по awk на http://ln.com.ua/~openxs/projects/man/uw2/awk.html.
Ведущий рассылки Big Shadow

http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу

В избранное