Работа с текстом |
Perl является прекрасным инструментом для обработки текстовой информации. На каждом компьютере всегда найдется парочка файлов-протоколов (логов) событий тех или иных приложений. Сисадмины знают, как утомительно выискивать в этих протоколах необходимую информацию или анализировать оную. Хорошо, если у вас на машине только один лог - bootlog.txt, а если десяток, и ежедневно надо шефу давать отчет, кто с какого компьютера куда лазил в Интернете, сколько было посещений вашего сайта, у кого в почтовом ящике 90% развлекательных рассылок с www.sexygirls.com, а 10% - служебной корреспонденции, то вам без инструментария не обойтись. Лучше всего для этих целей использовать perl, - за 10-15 минут можно написать любой анализатор лога на свой вкус. Анализатор логаК примеру, есть лог учета трафика машин вашей сети по работе в интернет (вполне реальный лог демона trafd). Обычно он имеет такой вид:from to bytes total date time ws-5.game.net ws-1.game.net 1884 3228 07.10.01 19:04:47 forwarder5.spylog. ws-1.game.net 2164 3164 07.10.01 19:04:47 ws-5.game.net 62.146.24.217 1802 3162 07.10.01 19:04:47 ws-5.game.net entropy.agava.ru 1315 3035 07.10.01 19:04:47 sovet.yandex.ru ws-1.game.net 2729 2933 07.10.01 19:04:47 forwarder5.spylog. ws-5.game.net 2409 2905 07.10.01 19:04:47 ws-1.game.net forwarder5.spylog. 1537 2861 07.10.01 19:04:47 ws-1.game.net search.rambler.ru 1006 2062 07.10.01 19:04:47 Задача: по определенной машине извлечь всю статистику - общее количество байт переданное и принятое извне, и список сайтов, с которыми данный хост (машина) обменивался данными. Как видим, запись лога состоит из 6 полей определенной длины, поля не разделяются. (Обычно поля разделяются табуляцией или запятыми). Еще определено, что в данном случае в значении поля не может быть пробела. Можно попробовать разделить поля по пробелам. Алгоритм прост = перебирать файл по строке, строку разбивать на поля, и, если совпадает с требуемым хостом, то суммировать данные. Проще простого. Если вы думаете, что сейчас начнутся циклы с нескончаемым уровнем вложенности, то заблуждаетесь - это оставьте другим языкам программирования. Тут намного проще:tr/ / /s; ($add1,$add2,$stemp,$totsize,$date,$time) = split(/ /,$_);Вот эти две строчки делают всю работу: сначала из строки ($_) удаляют лишние пробелы, оставляя по одному, и затем режет ее на переменные (split), используя в качестве разделителя пробелы. Если вы с perl не знакомы, то следует пояснить: переменные в perl в основном начинаются со знака доллара $, а переменную по "умолчанию" обозначают как $_. Хитрая конструкция tr/ / /s; обозначает следующее: найти в переменной "по умолчанию" все пробелы и сжать их tr/ / /s Чтение файлаЧтобы получить строки из файла, для этого его нужно только открыть и в цикле уже обрабатывать строки: open FILE,"trafd.log"; while (<FILE>) { tr/ / /s; ($add1,$add2,$stemp,$totsize,$date,$time) = split(/ /,$_); .... }Весьма непривычно и на первый взгляд, лишено смысла. Но, поверьте - работает и весьма эффективно. На тех же сях(язык C) эта обработка заняла бы по крайней мере в четыре раза больше кода. Здесь же конструкция while (<FILE>)эквивалентна такой (на абстрактном языке): while(!eof(FILE)) $str = readln(FILE); } Передача параметров в скриптДальше нужно лишь выделить нужные нам записи и просуммировать их траффик. Адрес нужного нам хоста мы будем передавать в командной строке. Кроме того, в качестве параметра можно передавать и имя лог-файла. Как этот параметр получить в perl скрипте:$req_addr = shift; $filename = shift;Чтобы было ясно: параметры получаем по очереди из командной строки, сдвигая их оператором shift. Это простейший метод, и при его использовании требуется соблюдать очередность параметров. Таким образом переменной $req_addr было присвоено значение первого параметра, а переменной $filename - значение второго параметра. ХэшиДалее - банальная проверка условия и подсчет траффика:$totalin = 0; $totalout= 0; if ($add1 eq $req_addr) { # подсчитываем входящий траффик $totalin += $totsize; # запомним хост-корреспондент и траффик на него $dh{$add2} += $totsize; } if ($add2 eq $req_addr) { # подсчитываем исходящий траффик $totalout += $totsize; # запомним хост-корреспондент и траффик на него $dh{$add1} += $totsize; }В условии использован оператор eq, т.е. оператор сравнения == для текстовых выражений. А хитрая конструкция вида $dh{$add1}+=$totsize записывает в именованный массив (хэш) переменную $totsize, причем оператором += суммирует первоначальное содержимое элемента хэша со значением, хранящемся в $totsize. Что такое хэш (hash)? Это массив, у каждого элемента которого есть собственное имя и значение, т.е.: %dh = ('www.yahoo.com', 9845, 'www.yandex.ru', 23632,.....); #можно записывать и так: %dh = ( www.yahoo.com=> 9845, www.yandex.ru=> 23632, ...=>.....);Нужно помнить, что хэш в целом начинается не с $, а с "процента" - %, но при прямом обращении к элементу нужно использовать запись вида $x{$y} = $value;. Полный текст скриптаВот и все куски кода готовы - нужно из них слепить программку - анализатор лога. Что у нас получится:#!/usr/bin/perl # первая строка в perl программе - всегда путь на диске к интерпретатору # в данном случае - в Unix - стиле, но можно, # например = c:\perl\bin\perl.exe # Хотя в MS Windows это не обязательно, если это не CGI скрипт. $req_addr = shift; # параметр - адрес хоста $filename = shift; # параметр - имя лог-файла print $filename,"\n"; if ( $filename eq 0 ) { # не передали имя лог-файла $filename = "/var/log/trafd.log"; # берем имя по умолчанию } print $filename,"::",$req_addr,"\n"; open FILE,$filename; # открываем файл $totalin = 0; $totalout= 0; while (<FILE>) { tr/ / /s; ($add1,$add2,$stemp,$totsize,$date,$time) = split(/ /,$_); if ($add1 =~ $req_addr) { $totalin += $totsize; $dh{$add2} += $totsize; } if ($add2 eq $req_addr) { $totalout += $totsize; $dh{$add1} += $totsize; } } close FILE; # не забыть закрыть файл # Последнее - распечатать результат print "Статистика по хосту ",$req_addr,"\n"; print "Исходящий траффик :",$totalin,"\n"; print "Входящий траффик :",$totalout,"\n"; print "\n"; print "В сумме по удаленным хостам:\n"; foreach $key (sort keys(%dh)) { foreach (split("\0", $dh{$key})) { ($out = $_); format STDOUT = @<<<<<<<<<<<<<<<<<<<<<<<< == @>>>>>>>>>>>>>>>> байт $key, $out . write(); } }Всего-то - 45 строк с комментариями! Последнее пояснение - конструкция Форматированный выводformat STDOUT =. Это, как вы догадались, строка форматирования вывода в файл STDOUT (т.е. на экран по умолчанию).В шаблоне скобки <<<<<< указывают выравнивание переменной по левому краю, скобки >>>>>> - по правому. Если использовать символы |||||||, то это обозначит выравнивание по центру, ####.## - вывод числа с N знаками после запятой. В поле будет выводится столько символов, сколько есть в строке шаблона. Если вместо знака @ перед шаблонами поставить @*, то данная переменная будет выводится в колонку, если не поместится на одной строке. Если вместо @ использовать ^, то выведется символов столько, сколько есть в строке шаблона, а переменная будет содержать остаток невыведенных символов. Программа готова, можно пользоваться и особых любителей в рабочее время лазить по сайтам сомнительного содержания можно будет "пощемить", чтобы не загружали траффик и не мешали сисадмину рубиться в "Counter-Strike". Предыдущая страница Главная |