<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Журнал системного программиста &#187; perl</title>
	<atom:link href="http://sadko.xskernel.org/archives/tag/perl/feed" rel="self" type="application/rss+xml" />
	<link>http://sadko.xskernel.org</link>
	<description>блог обо всём и ни о чём</description>
	<lastBuildDate>Wed, 14 Dec 2011 20:21:22 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Продолжаю развивать проектик.</title>
		<link>http://sadko.xskernel.org/archives/1774</link>
		<comments>http://sadko.xskernel.org/archives/1774#comments</comments>
		<pubDate>Fri, 21 May 2010 20:05:00 +0000</pubDate>
		<dc:creator>SadKo</dc:creator>
				<category><![CDATA[Интернеты]]></category>
		<category><![CDATA[Кодинг]]></category>
		<category><![CDATA[日本語]]></category>
		<category><![CDATA[firebird]]></category>
		<category><![CDATA[kanji]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[perl]]></category>

		<guid isPermaLink="false">http://sadko.xskernel.org/?p=1774</guid>
		<description><![CDATA[Tweet Собственно, как и было сказано ранее, я для упрощения изучения японского языка сделал такой вот сайтец &#8212; http://kanji.xskernel.org/. И обещал обновить его структуру. Сегодня залил свежий движок на пропатченную базу. Теперь осталось выпилить из базы лишние таблицы, которые уже не играют никакой роли. Наконец-то заработал словарь, который по мере изучения мною языка потихоньку пополняется [...]]]></description>
			<content:encoded><![CDATA[<div style="float: right; margin: 0 0 5px 10px; " class="vk-button">
<!-- vkontakte share button -->
<script type="text/javascript">
<!--
document.write(VK.Share.button(
{
  url: 'http://sadko.xskernel.org/archives/1774',
  title: 'Продолжаю развивать проектик.',
  description: 'Собственно, как и было сказано ранее, я для упрощения изучения японского языка сделал такой вот сайтец - http://kanji.xskernel.org/. И обещал обновить его структуру. Сегодня залил свежий движок на пропатч...',
  noparse: true 
}, 
{
  type: 'button',
  text: 'Скопировать'
}));
-->
</script>
<!-- / vkontakte share button -->
</div><div style="height:21px; padding-top:2px;" class="really_simple_share"><div style="float:left; " class="really_simple_share_linkedin"> 
				<script type="text/javascript" src="http://platform.linkedin.com/in.js"></script>
				<script type="in/share" data-counter="right" data-url="http://sadko.xskernel.org/archives/1774"></script>
			</div><div style="float:left; padding-left:10px;" class="really_simple_share_twitter"> 
				<a href="http://twitter.com/share" class="twitter-share-button" data-count="horizontal" 
					data-text="&ETH;&Ntilde;&ETH;&frac34;&ETH;&acute;&ETH;&frac34;&ETH;&raquo;&ETH;&para;&ETH;&deg;&Ntilde; &Ntilde;&ETH;&deg;&ETH;&middot;&ETH;&sup2;&ETH;&cedil;&ETH;&sup2;&ETH;&deg;&Ntilde;&Ntilde; &ETH;&iquest;&Ntilde;&ETH;&frac34;&ETH;&micro;&ETH;&ordm;&Ntilde;&ETH;&cedil;&ETH;&ordm;." data-url="http://sadko.xskernel.org/archives/1774">Tweet</a> 
				<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script> 
			</div></div>
	<br style="clear:both;" /><p>Собственно, как и было сказано ранее, я для упрощения изучения японского языка сделал такой вот сайтец &#8212; <a href="http://kanji.xskernel.org/">http://kanji.xskernel.org/</a>. И обещал обновить его структуру. Сегодня залил свежий движок на пропатченную базу. Теперь осталось выпилить из базы лишние таблицы, которые уже не играют никакой роли. Наконец-то заработал словарь, который по мере изучения мною языка потихоньку пополняется новыми словами. Идею, предложенную товарищем <a href="http://users.livejournal.com/stalkerg/profile/"><img src="http://stat.livejournal.com/img/userinfo.gif" style="border: 0pt none ; vertical-align: bottom;" height="17" width="17"></a><a href="http://users.livejournal.com/stalkerg/"><b>stalkerg</b></a> я постарался воплотить по-максимуму. А именно: сделал возможным поиск слова не только по кандзи, но и по фонетической составляющей (то есть, можно вписать слово каной либо ромадзи, при этом добавлена ещё одна фича: если вы не знаете, является гласный звук долгим или коротким, то словарь выдаст вам оба результата, если все гласные звуки будут введены как короткие).<br />
Соответственно, раньше движок задумывался как запоминалка канзди, теперь же он всё больше и больше становится полноценным словарём, поэтому возможна трансляция как из японского в русский, так и из русского в японский. Единственное, что меня пока смущает, &#8212; это дополнения к слову. Их, скорее, следует переделать (вернее группировать), и я уже примерно представляю как. Но это позже. Сейчас меня больше беспокоит отсутствие админки, ввиду чего данные мне приходится пока по-прежнему вбивать специально написанными консольными скриптами.<br />
Также в самом движке добавил задел на поддержку не только СУБД <a href="http://dev.mysql.com/">MySQL</a>, но и СУБД <a href="http://firebirdsql.org/">Firebird</a>. Если будет время &#8212; обязательно продолжу развитие движка в этом направлении. В общем, планов море, а времени и сил &#8212; чуть.<br />
Опять же, если есть какие замечания/пожелания по поводу сайта, прошу писать отзывы.<br />
И, напоследок. В процессе написания движка сайта (а пишется он на <a href="http://perl.org/">PERL</a>), мне пришлось решить ряд задач, о которых могут выйти вполне интересные статьи. Если аудитории интересно &#8212; то я могу потихоньку выкладывать тут упрощённые кусочки движка с объяснением, как это работает. Заодно и блог оживёт, пожалуй.</p>
]]></content:encoded>
			<wfw:commentRss>http://sadko.xskernel.org/archives/1774/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>For fun.</title>
		<link>http://sadko.xskernel.org/archives/1603</link>
		<comments>http://sadko.xskernel.org/archives/1603#comments</comments>
		<pubDate>Fri, 20 Nov 2009 19:01:40 +0000</pubDate>
		<dc:creator>SadKo</dc:creator>
				<category><![CDATA[Кодинг]]></category>
		<category><![CDATA[Свободное ПО]]></category>
		<category><![CDATA[bsd]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[opensuse]]></category>
		<category><![CDATA[perl]]></category>

		<guid isPermaLink="false">http://sadko.xskernel.org/?p=1603</guid>
		<description><![CDATA[Tweet Совсем недавно приходилось решать интересную задачу по Unix: Фактически нужно было разработать демон simple-telnetd на языке Perl, позволяющий удалённо запускать некоторое ограниченное подмножество команд и выводить пользователю результат их выполнения. simple-telnetd может запускать только разрешенные программы, которые перечислены в файле /etc/simple-telnetd.conf. Демон должен перечитывать этот файл и обновлять список разрешенных программ после поступления сигнала [...]]]></description>
			<content:encoded><![CDATA[<div style="float: right; margin: 0 0 5px 10px; " class="vk-button">
<!-- vkontakte share button -->
<script type="text/javascript">
<!--
document.write(VK.Share.button(
{
  url: 'http://sadko.xskernel.org/archives/1603',
  title: 'For fun.',
  description: 'Совсем недавно приходилось решать интересную задачу по Unix:\nФактически нужно было разработать демон simple-telnetd на языке Perl, позволяющий удалённо запускать некоторое ограниченное подмножество ...',
  noparse: true 
}, 
{
  type: 'button',
  text: 'Скопировать'
}));
-->
</script>
<!-- / vkontakte share button -->
</div><div style="height:21px; padding-top:2px;" class="really_simple_share"><div style="float:left; " class="really_simple_share_linkedin"> 
				<script type="text/javascript" src="http://platform.linkedin.com/in.js"></script>
				<script type="in/share" data-counter="right" data-url="http://sadko.xskernel.org/archives/1603"></script>
			</div><div style="float:left; padding-left:10px;" class="really_simple_share_twitter"> 
				<a href="http://twitter.com/share" class="twitter-share-button" data-count="horizontal" 
					data-text="For fun." data-url="http://sadko.xskernel.org/archives/1603">Tweet</a> 
				<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script> 
			</div></div>
	<br style="clear:both;" /><p>Совсем недавно приходилось решать интересную задачу по Unix:<br />
Фактически нужно было разработать демон simple-telnetd на языке Perl, позволяющий удалённо запускать некоторое ограниченное подмножество команд и выводить пользователю результат их выполнения. </p>
<ul>
<li>simple-telnetd может запускать только разрешенные программы, которые перечислены в файле /etc/simple-telnetd.conf. Демон должен перечитывать этот файл и обновлять список разрешенных программ после поступления сигнала SIGHUP;</li>
<li>Запускаемые программы могут иметь параметры командной строки, но simple-telnetd не должен поддерживать интерактивного взаимодействия пользователя с запускаемыми программами;</li>
<li>Демон не обязан обрабатывать спец символы: ^C, ^D, и т.д.</li>
<li>В качестве параметра командной строки simple-telnetd может передаваться параметр timeout &#8212; максимальное время выполнения одной команды;</li>
<li>Желательно чтобы демон мог прослушивать не только tcp сокеты, но и локальные (например /tmp/simple-telnetd);</li>
</ul>
<p>В конце-концов, скрипт всё же не пригодился, поэтому, чтобы компенсировать потраченное на него время, выкладываю его тут &#8212; вдруг, кому пригодится. Забрать его можно по этой ссылке.<br />
<a href="http://download.xskernel.org/pub/perl/simple-telnetd.tar.gz">simple-telnetd.tar.gz</a><br />
Если кому-то помогло, пишите, не стесняйтесь, буду рад <img src='http://sadko.xskernel.org/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
<p>Поскольку я писал этот демон с особым энтузиазмом, не обошлось без фич, которых нет в исходном задании: написал init.d-скрипт для службы, который успешно был протестирован в openSUSE Linux, оформил perldoc и manpages, сделал возможность интерактивной авторизации, ну и другая мелочёвка (уже даже и не помню какая) вдовесок.</p>
]]></content:encoded>
			<wfw:commentRss>http://sadko.xskernel.org/archives/1603/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Hand-made синхронизация репозиториев Subversion</title>
		<link>http://sadko.xskernel.org/archives/1050</link>
		<comments>http://sadko.xskernel.org/archives/1050#comments</comments>
		<pubDate>Sun, 29 Mar 2009 13:25:00 +0000</pubDate>
		<dc:creator>SadKo</dc:creator>
				<category><![CDATA[XSystem]]></category>
		<category><![CDATA[Кодинг]]></category>
		<category><![CDATA[Одминское]]></category>
		<category><![CDATA[Туториалы]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[svn]]></category>
		<category><![CDATA[xsystem]]></category>

		<guid isPermaLink="false">http://sadko.xskernel.org/archives/1050</guid>
		<description><![CDATA[Tweet Несмотря на то, что репозиторий ОС XSystem мигрировал ко мне на домашний сервер, всё же хорошо было бы хранить бэкапы на каком-нибудь удалённом сервере. Для этого я решил воспользоваться уже имеющимся репозиторием subversion на http://sourceforge.net/. Идея работы такая: раз в сутки мой репозиторий должен синхронизироваться с удалённым репозиторием. При этом, все изменения в одном [...]]]></description>
			<content:encoded><![CDATA[<div style="float: right; margin: 0 0 5px 10px; " class="vk-button">
<!-- vkontakte share button -->
<script type="text/javascript">
<!--
document.write(VK.Share.button(
{
  url: 'http://sadko.xskernel.org/archives/1050',
  title: 'Hand-made синхронизация репозиториев Subversion',
  description: 'Несмотря на то, что репозиторий ОС XSystem мигрировал ко мне на домашний сервер, всё же хорошо было бы хранить бэкапы на каком-нибудь удалённом сервере.\nДля этого я решил воспользоваться уже имею...',
  noparse: true 
}, 
{
  type: 'button',
  text: 'Скопировать'
}));
-->
</script>
<!-- / vkontakte share button -->
</div><div style="height:21px; padding-top:2px;" class="really_simple_share"><div style="float:left; " class="really_simple_share_linkedin"> 
				<script type="text/javascript" src="http://platform.linkedin.com/in.js"></script>
				<script type="in/share" data-counter="right" data-url="http://sadko.xskernel.org/archives/1050"></script>
			</div><div style="float:left; padding-left:10px;" class="really_simple_share_twitter"> 
				<a href="http://twitter.com/share" class="twitter-share-button" data-count="horizontal" 
					data-text="Hand-made &Ntilde;&ETH;&cedil;&ETH;&frac12;&Ntilde;&Ntilde;&ETH;&frac34;&ETH;&frac12;&ETH;&cedil;&ETH;&middot;&ETH;&deg;&Ntilde;&ETH;&cedil;&Ntilde; &Ntilde;&ETH;&micro;&ETH;&iquest;&ETH;&frac34;&ETH;&middot;&ETH;&cedil;&Ntilde;&ETH;&frac34;&Ntilde;&ETH;&cedil;&ETH;&micro;&ETH;&sup2; Subversion" data-url="http://sadko.xskernel.org/archives/1050">Tweet</a> 
				<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script> 
			</div></div>
	<br style="clear:both;" /><p>Несмотря на то, что репозиторий <a href="http://xskernel.org/">ОС XSystem</a> мигрировал ко мне на домашний сервер, всё же хорошо было бы хранить бэкапы на каком-нибудь удалённом сервере.<br />
Для этого я решил воспользоваться уже имеющимся репозиторием subversion на <a href="http://sourceforge.net/">http://sourceforge.net/</a>.</p>
<p>Идея работы такая: раз в сутки мой репозиторий должен синхронизироваться с удалённым репозиторием. При этом, все изменения в одном репозитории должны однозначно отражаться на другом. С версии subversion 1.4 появилась такая тулза, как <b>svnsync</b>, но она для моего случая не подходит: оба репозитория не предоставляют прямого доступа к svnroot.</p>
<div text="Мой solution..." class="ljcut">Поэтому, немного порывшись в гугле, решил прибегнуть к собственному механизму синхронизации. Идея простая: раз в сутки сливается содержимое первого и второго (будем называть source и destination) репозитория. После этого сверяем содержимое source-репозитория с содержимым destination-репозитория: создаём несуществующие каталоги и копируем несуществующие файлы. Файлы, имеющие различные контрольные суммы md5, также перезаписываем. Но это не всё. После того, как мы влили новое файло в локальное дерево destination-репозитория, мы должны удалить из него те файлы, которых уже нет в source-репозитории. Для этого осуществляем обратную сверку снимка destination-репозитория с source-репозиторием и удаляем из него лишние файлы.</p>
<p>В результате всё это было сведено к скрипту на PERL.</p>
<pre>
#!/usr/bin/perl

use strict;

my @blacklist   =
(
    qr(^\.svn|\/\.svn$),
    qr(^\.{1,2}$|\/\.{1,2}$)
);

my $src_path  = 'xsystem';
my $dst_path  = 'xskernel-sync';
my $svn_user  = '<destination-username>';
my $svn_pass  = '<destination-password>';

# Update repositories
`svn update $src_path`;
`svn update $dst_path`;

# now search files in src_path and compare to dst_path

add_files();
remove_files();

`svn commit $dst_path -m &quot;Synchronization commit&quot; --username $svn_user --password $svn_pass`;</destination-password></destination-username></pre>
<p>Как видно, работа скрипта достаточно примитивна. Для его работы нужно иметь два рабочих снимка репозитория: source и destination. У меня source-репозиторий находится в каталоге xsystem, а destination &#8212; в каталоге dst_path.<br />
Скрипт сначала обновляет снимки репозиториев, затем осуществляет вливание новых файлов в destination-репозиторий при помощи функции add_files(). После этого удаляются устаревшие файлы и каталоги из destination-репозитория функцией remove_files() и производится коммит изменений в destination-репозиторий.</p>
<p>Дело осталось за малым &#8212; разобрать функции добавления и удаления файлов и каталогов. Для этого сначала напишем функцию, которая получает md5sum файла:</p>
<pre>
sub md5sum
{
    my $fname = shift;
    if (open PIPE, &quot;md5sum $fname |&quot;)
    {
        my $line =
<pipe>;
        close PIPE;
        my ($sum) = ($line =~ /^(\w+)\s+/o);
        return $sum;
    }
    return undef;
}</pipe></pre>
<p>В принципе, ничего нового. Вызывается утилита md5sum и анализируется её вывод.</p>
<p>Также необходимо игнорировать каталоги &quot;.svn&quot;, &quot;.&quot; и &quot;..&quot;, для чего вводится массив @blacklist и пишется функция банинга файлов:</p>
<pre>
sub ban_file
{
    my $src_file = shift;
    foreach (@blacklist)
    {
        ($src_file =~ $_) and
            return 1;
    }
    return undef;
}</pre>
<p>После этого можно спокойно разобрать функцию add_files():</p>
<pre>

sub add_files
{
    my @directories = ();
    my $curr_dir = '';

    do
    {
        # Открываем каталог
        if (opendir DIRHDL, &quot;$src_path$curr_dir&quot;)
        {
            CYCLE: # Читаем содержимое каталога
            while (my $fname = readdir DIRHDL)
            {
                # Баним ненужные файлы
                my $src_file = &quot;$src_path$curr_dir/$fname&quot;;
                (ban_file($src_file)) and
                    next CYCLE;

                my $dst_short = &quot;$curr_dir/$fname&quot;;
                my $dst_file  = &quot;$dst_path$dst_short&quot;;

                # Файл является каталогом?
                if (-d $src_file)
                {
                    # необходимо проверить, что он есть в destination-репозитории
                    unless (-d $dst_file)
                    {
                        # если каталога нет - его нужно создать
                        print &quot;mkdir   $dst_short\n&quot;;
                        `mkdir -p $dst_file`;
                        `svn add $dst_file`;
                    }

                    # Запомним каталог для того, чтобы в будущем его просмотреть
                    push @directories, $dst_short;
                }
                else
                {
                    # проверяем, существует ли файл в destination-репозитории
                    unless (-e $dst_file)
                    {
                        # файл не существует, его нужно скопировать и добавить
                        print &quot;add     $dst_short\n&quot;;
                        `cp -f $src_file $dst_file`;
                        `svn add $dst_file`;
                    }
                    else
                    {
                        # файл существует, вычисляем md5sum обоих файлов
                        my $sum1 = md5sum($src_file);
                        my $sum2 = md5sum($dst_file);

                        # если суммы не совпадают - заменяем файл новым
                        if ($sum1 ne $sum2)
                        {
                            print &quot;replace $dst_short\n&quot;;
                            `cp -f $src_file $dst_file`;
                        }
                    }
                }
            }
        }
        else
        {
            print &quot;Can't open dir $src_path$curr_dir\n&quot;;
        }

        # закрываем каталог и получаем следующий каталог, который следует обработать
        closedir DIRHDL;
        $curr_dir = shift @directories;
    }
    while (defined $curr_dir); # крутимся, пока в списке присутствуют обрабатываемые каталоги.
}</pre>
<p>При удалении функция будет очень похожа, только теперь надо осуществлять поиск файлов в destination-репозитории и смотреть, есть ли они в source-репозитории:</p>
<pre>
sub remove_files
{
    my @directories = ();
    my $curr_dir = '';

    do
    {
        # опять же, открываем каталог destination-репозитория
        if (opendir DIRHDL, &quot;$dst_path$curr_dir&quot;)
        {
            CYCLE: # читаем файлы
            while (my $fname = readdir DIRHDL)
            {
                # баним недопустимые diren'ы
                my $src_file = &quot;$dst_path$curr_dir/$fname&quot;;
                (ban_file($src_file))
                    and next CYCLE;

                my $src_short = &quot;$curr_dir/$fname&quot;;
                my $dst_file  = &quot;$src_path$src_short&quot;;

                # проверяем, является ли это каталогом
                if (-d $src_file)
                {
                    # проверяем, существует ли этот каталог в source-репозитории
                    unless (-d $dst_file)
                    {
                        # каталога нет - удаляем его из destination-репозитория
                        print &quot;rmdir   $src_short\n&quot;;
                        `svn delete $src_file`;
                    }
                    else
                    {
                        # каталог существует, запоминаем его для дальнейшей обработки
                        push @directories, $src_short;
                    }
                }
                else
                {
                    # Это обычный файл, проверяем, есть ли он в source-репозитории
                    unless (-e $dst_file)
                    {
                        # Файла нет, удаляем его из destination-репозитория
                        print &quot;unlink  $src_short\n&quot;;
                        `svn delete $src_file`;
                    }
                }
            }
        }
        else
        {
            print &quot;Can't open dir $dst_path$curr_dir\n&quot;;
        }

        # Закрываем каталог, получаем следующий
        closedir DIRHDL;
        $curr_dir = shift @directories;
    }
    while (defined $curr_dir);
}
</pre>
</div>
<p>Недостаток этой тулзы один: если файл был перемещён, то он будет распознан как новый файл. Хотя, я не думаю, что файлы и каталоги так часто перемещаются в логически корректно построенном дереве проекта.</p>
]]></content:encoded>
			<wfw:commentRss>http://sadko.xskernel.org/archives/1050/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Статистика.</title>
		<link>http://sadko.xskernel.org/archives/1027</link>
		<comments>http://sadko.xskernel.org/archives/1027#comments</comments>
		<pubDate>Sun, 11 Jan 2009 18:06:00 +0000</pubDate>
		<dc:creator>SadKo</dc:creator>
				<category><![CDATA[Кодинг]]></category>
		<category><![CDATA[Одминское]]></category>
		<category><![CDATA[Свободное ПО]]></category>
		<category><![CDATA[apache2]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[perl]]></category>

		<guid isPermaLink="false">http://sadko.xskernel.org/archives/1027</guid>
		<description><![CDATA[Tweet Стремление товарища mar1ner&#160; наблюдать за поисковыми запросами также сподвигло меня на идею написания скрипта, который составлял бы суточную статистику переходов по поисковым запросам на мой сервер. Скрипт запускается в час ночи по локальному времени (cron) и собирает информацию за прошедшие сутки. Запуск в час ночи сделал с поправкой на переход с летнего времени на [...]]]></description>
			<content:encoded><![CDATA[<div style="float: right; margin: 0 0 5px 10px; " class="vk-button">
<!-- vkontakte share button -->
<script type="text/javascript">
<!--
document.write(VK.Share.button(
{
  url: 'http://sadko.xskernel.org/archives/1027',
  title: 'Статистика.',
  description: 'Стремление товарища mar1ner&nbsp; наблюдать за поисковыми запросами также сподвигло меня на идею написания скрипта, который составлял бы суточную статистику переходов по поисковым запросам на мо...',
  noparse: true 
}, 
{
  type: 'button',
  text: 'Скопировать'
}));
-->
</script>
<!-- / vkontakte share button -->
</div><div style="height:21px; padding-top:2px;" class="really_simple_share"><div style="float:left; " class="really_simple_share_linkedin"> 
				<script type="text/javascript" src="http://platform.linkedin.com/in.js"></script>
				<script type="in/share" data-counter="right" data-url="http://sadko.xskernel.org/archives/1027"></script>
			</div><div style="float:left; padding-left:10px;" class="really_simple_share_twitter"> 
				<a href="http://twitter.com/share" class="twitter-share-button" data-count="horizontal" 
					data-text="&ETH;&iexcl;&Ntilde;&ETH;&deg;&Ntilde;&ETH;&cedil;&Ntilde;&Ntilde;&ETH;&cedil;&ETH;&ordm;&ETH;&deg;." data-url="http://sadko.xskernel.org/archives/1027">Tweet</a> 
				<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script> 
			</div></div>
	<br style="clear:both;" /><p>Стремление товарища <a href="http://mar1ner.livejournal.com/" class="lj-user">mar1ner</a>&nbsp; наблюдать за поисковыми запросами также сподвигло меня на идею написания скрипта, который составлял бы суточную статистику переходов по поисковым запросам на мой сервер. Скрипт запускается в час ночи по локальному времени (cron) и собирает информацию за прошедшие сутки. Запуск в час ночи сделал с поправкой на переход с летнего времени на зимнее и обратно <img src='http://sadko.xskernel.org/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> ,&nbsp;на всякий пожарный.</p>
<p>Собственно, сам скрипт выглядит так:<br /><span id="more-1027"></span><br />#!/usr/bin/perl</p>
<p>use strict;</p>
<p>my %search_regexp =<br />(<br />&nbsp;&nbsp;&nbsp; &#8216;rambler&#8217;&nbsp;&nbsp;&nbsp;&nbsp; =&gt; &#8216;nova\.rambler\.ru\/search\?.*query&#8217;,<br />&nbsp;&nbsp;&nbsp; &#8216;yandex&#8217;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; =&gt; &#8216;yandex\.ru\/yandsearch\?.*text&#8217;,<br />&nbsp;&nbsp;&nbsp; &#8216;google&#8217;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; =&gt; &#8216;.*google.*\/search\?.*\&amp;q&#8217;,<br />&nbsp;&nbsp;&nbsp; &#8216;msn&#8217;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; =&gt; &#8216;search\.live\.com/results.aspx\?q&#8217;,<br />&nbsp;&nbsp;&nbsp; &#8216;yahoo&#8217;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; =&gt; &#8216;search\.yahoo\.com/search\?p&#8217;,<br />&nbsp;&nbsp;&nbsp; &#8216;nigma.ru&#8217;&nbsp;&nbsp;&nbsp; =&gt; &#8216;nigma\.ru\/index.php\?s&#8217;,<br />&nbsp;&nbsp;&nbsp; &#8216;yes.ru&#8217;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; =&gt; &#8216;search\.yes\.ru\/index.php\?s&#8217;,<br />&nbsp;&nbsp;&nbsp; &#8216;altavista&#8217;&nbsp;&nbsp; =&gt; &#8216;.*altavista\.com\/web\/results\?.*\&amp;q&#8217;<br />);</p>
<p>my $log_file&nbsp; = &#8216;/var/log/apache2/access_log&#8217;;<br />my $dest_mail = &#8216;mail@domain.org&#8217;;<br />my $mail_prog = &#8216;/usr/sbin/sendmail -t&#8217;;</p>
<p># Get date<br />my ($day, $month, $year, $wday) = (localtime(time()-2*60*60))[3..6]; # subtract 2 hours</p>
<p>$year&nbsp; += 1900;<br />$day&nbsp;&nbsp;&nbsp; = sprintf(&quot;%02d&quot;, $day);<br />$month&nbsp; = (&#8216;Jan&#8217;, &#8216;Feb&#8217;, &#8216;Mar&#8217;, &#8216;Apr&#8217;, &#8216;May&#8217;, &#8216;Jun&#8217;, &#8216;Jul&#8217;, &#8216;Aug&#8217;, &#8216;Sep&#8217;, &#8216;Oct&#8217;, &#8216;Nov&#8217;, &#8216;Dec&#8217;)[$month];<br />$wday&nbsp;&nbsp; = (&#8216;Sun&#8217;, &#8216;Mon&#8217;, &#8216;Tue&#8217;, &#8216;Wed&#8217;, &#8216;Thu&#8217;, &#8216;Fri&#8217;, &#8216;Sat&#8217;)[$wday];</p>
<p># replace patterns<br />foreach (values %search_regexp)<br />{<br />&nbsp;&nbsp;&nbsp; $_ = qr(^\d+\.\d+\.\d+\.\d+[^\[]*\[$day\/$month\/$year[^\]]*\]\s*\&quot;GET [^\&quot;]*\&quot;[^\&quot;]*\&quot;http:\/\/$_=([^\&amp;^\&quot;]*)[\&amp;\&quot;]);<br />}</p>
<p>my $result_hash = { };</p>
<p>if (open LOG_FILE, &#8216;&lt;&#8217;, $log_file)<br />{<br />&nbsp;&nbsp;&nbsp; while (my $line = &lt;LOG_FILE&gt;)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach my $key (keys %search_regexp)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (my ($request) = ($line =~ $search_regexp{$key}))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; my $counters = ($result_hash-&gt;{$key} ||= {});<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $request =~ s/\+/ /g;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $request =~ s/\%([a-fA-F0-9]{2})/pack(&#8216;C&#8217;,hex($1))/eg;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $counters-&gt;{$request} ||= 0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $counters-&gt;{$request}++;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; last;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; close LOG_FILE;<br />}<br />else<br />{<br />&nbsp;&nbsp;&nbsp; print &quot;Could not open log file\n&quot;;<br />}</p>
<p># Now make a mail with information<br />open MAILER, &quot;| $mail_prog -t&quot;;</p>
<p>print MAILER &lt;&lt;MAIL_EOF;<br />Content-Type: text/html; charset=UTF-8; format=flowed<br />Content-Transfer-Encoding: 8bit<br />To: $dest_mail<br />From: webserver\@xskernel.org<br />Subject: Daily Search Engine Reference Log.<br />&lt;html&gt;&lt;body&gt;<br />&lt;h1&gt;Daily Search Engine Reference Log.&lt;/h1&gt;<br />MAIL_EOF</p>
<p>foreach my $engine (keys %$result_hash)<br />{<br />&nbsp;&nbsp;&nbsp; print MAILER &quot;&lt;h2&gt;Engine: $engine&lt;/h2&gt;&quot;;<br />&nbsp;&nbsp;&nbsp; print MAILER &quot;&lt;table width=640&gt;&lt;tr&gt;&lt;th align=\&quot;left\&quot; bgcolor=\&quot;#96f198\&quot; width=\&quot;550\&quot;&gt;&lt;b&gt;Request&lt;/b&gt;&lt;/th&gt;&lt;th align=\&quot;center\&quot; bgcolor=\&quot;#96f198\&quot; width=\&quot;90\&quot;&gt;&lt;b&gt;Count&lt;/b&gt;&lt;/th&gt;\n&quot;;</p>
<p>&nbsp;&nbsp;&nbsp; while (my ($request, $count) = each %{$result_hash-&gt;{$engine}})<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print MAILER &quot;&lt;tr bgcolor=\&quot;#eef196\&quot;&gt;&lt;td align=\&quot;left\&quot;&gt;$request&lt;/td&gt;&lt;td align=\&quot;center\&quot;&gt;$count&lt;/td&gt;\n&quot;;<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; print MAILER &quot;&lt;/table&gt;\n&quot;;<br />}</p>
<p>print MAILER &quot;&lt;/html&gt;&lt;/body&gt;&quot;;</p>
<p>close MAILER;</p>
<p>Может быть, кому-нибудь тоже пригодится <img src='http://sadko.xskernel.org/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> .</p>
<p>Список поисковиков взял тот, что помню, а также тех, что мне подсказали мои товарищи. Если есть ещё какие популярные поисковики,&nbsp;подскажите,&nbsp;может добавлю и их <img src='http://sadko.xskernel.org/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> .<br />Кстати, aport.ru на запрос &quot;XSystem Kernel Team&quot; выдал фейл,&nbsp;не дав ни одной ссылки на официальный сайт. </p>
<p>З.Ы. Вообще,&nbsp;я не сторонник извращений на Perl и стараюсь делать код более понятным <img src='http://sadko.xskernel.org/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> , с развёрнутыми циклами и прочей ерундой.</p>
]]></content:encoded>
			<wfw:commentRss>http://sadko.xskernel.org/archives/1027/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

