― Web Technology and Life ―

Perlのログパース用モジュールRegexp::Log::Commonのサンプルスクリプトがわかりづらい

2012-04-07
Regexp::Log::CommonのSYNOPSISをみていて、カッコいいハック使っているコードって書いている側からすると気持良いけど、見ている側からするときもいよなーと改めて思ったのでメモです。

該当のコード

Regexp::Log::Commonのドキュメント見ると以下のようなコードがサンプルとして載っているんですね。

my $foo = Regexp::Log::Common->new(
    format  => 'custom %date %request',
    capture => [qw( ts request )],
);

# the format() and capture() methods can be used to set or get
$foo->format('custom %date %request %status %bytes');
$foo->capture(qw( ts req ));

# this is necessary to know in which order
# we will receive the captured fields from the regexp
my @fields = $foo->capture;

# the all-powerful capturing regexp :-)
my $re = $foo->regexp;

while (<>) {
    my %data;
    @data{@fields} = /$re/;    # no need for /o, it's a compiled regexp

    # now munge the fields
    ...
}

「んっ?」と思ったのが、この部分、

while (<>) {
    my %data;
    @data{@fields} = /$re/;
}

コードの解説

上記のコードには、わたしがあまり使わない4つのPerl流のハックが含まれています。 解説をつけると以下のようになります。

while (<>) {#ファイルハンドルからデータを取得
    my $row = $_;#実は$_というのが省略されている

    my %data;

    @data{@fields} = $row =~ /$re/;
    #$reの正規表現とパターンマッチした$rowのデータが配列で返ってきて、
    #ハッシュスライスでハッシュのキーの中に、
    #返ってきた配列のデータを入れている
}

ハッシュスライスとか、正規表現でパターンマッチしたデータを引数としてパターマッチ演算の結果を引数として受け取るとか、whileでファイルハンドルからデータを取得するとか、あまり見ないようなコードの中で、$_を省略するという省エネコードをやっているので、ちょっとぎょっとします。

驚き最小の法則

で、本題ですが、なんかこういうコードは書いているととても気持ちいいんですが(「オレfizzbuzzでも短いコード書けるぜ!」って案にアピールできるし、わかっているうちは短いコードのほうが見通し良くて気分爽快だし)、なんか久しぶりに開けてみると、「あちゃー」ってなるコードだと思うんですねー、特によく知らない人に「これなに?」って言われたときなんか。

そういう意味で、「驚き最小の法則」がやっぱり大事だなー、と思うわけですねー。わたしは、map関数使うの大好きだからついついわかりづらいコード書きがちなんですが、こういう風に人のコードを見ることで「あー、なんか自分イタイことをしておるなー」と再自覚したわけでした。

雑感

Perlに限った話でなく、JSとかもいろいろなライブラリ出すぎてキモいなと思う今日この頃というのもわざわざブログにした動機ですね。とわいえ、ハック的なコードの積み重ねで、すごいライブラリも出てきたりするわけで、使いどころはあるんですが、そのあたりは個人的な好みが入る部分が大きいので(一応、Perlには『Perlベストプラクティス』ってあってゆるいコーディング規約みたいになっているけど、JavaScriptだとなんかあるのかなー)、とりあえず、ぼくは「$_」の省略は、ないなーと思いました。

【追記:2013/10/04】使い方


#!/usr/bin/env perl
use strict;
use warnings;
use utf8;
use Regexp::Log::Common;
use DateTime::Format::HTTP;
use DateTime::TimeZone;


my $parser = Regexp::Log::Common->new(
    format  => ':extended',
    capture => [qw(host rfc authuser ts req status bytes ref ua)],
);

my $re     = $parser->regexp;
my @fields = $parser->capture;


while( my $row = ) {
    chomp $row;
    my %access_log; @access_log{@fields} = $row =~ /$re/;

    my $dt = DateTime::Format::HTTP->parse_datetime($access_log{ts});
    $dt->set_time_zone(DateTime::TimeZone->new(name => 'Asia/Tokyo'));

    print $access_log{host}."\t".$dt->datetime."\t".$access_log{ua}."\n";
}

こんな感じに書いて、


cat ./access.log | perl log_parse.pl

って実行すればいい感じに出力されますね。

Perl update_at : 2013-10-04T18:02:00
hirobanex.netの更新情報の取得
 RSSリーダーで購読する   
blog comments powered by Disqus