Работа с текстом

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".
Предыдущая страница  Главная
Сайт создан в системе uCoz