まさはる's Weblog

まさはる's Weblog
Macintoshとオーディオの話題

2004年7月10日

Perlを使ってみました(その3)

Category : Terminal / Posted at 2004年7月10日 21:10

 ようやく、ハッシュの使い方とソートの仕方を覚えたので、データの出現頻度が高い順にソートして出力するブログラムに改良しました。

#!/usr/bin/perl -w
use strict;
# ログファイルの整理プログラム log.pl [-p|r|h|P|H] ver.1.2
 
# 入力するログファイルのフォーマット
# host ident authuser date "request" status bytes "referer"
# "user-agent"\n
# date = [day/month/year:hour:minute:second zone]
# day 2桁, month 3文字, year 4桁, hour 2桁, minute 2桁, second 2桁
# zone (+|-)4桁
# request = command pass HTTP/*.*
# status 3桁
 
# 出力オプション
# p アクセス先を回数でソート(標準)
# ********** 999999 pass(前半40文字)
# r アクセス元を回数でソート
# ********** 999999 referer(前半40文字)
# h アクセス者を回数でソート
# ********** 999999 host(後半40文字)
# P クライアントエラーを出したアクセス先を回数でソート
# ********** 999999 pass(前半40文字)
# H クライアントエラーを出したアクセス者を回数でソート
# ********** 999999 host(後半40文字)
 
# 回数をカウントするリクエストのパスの末尾(-p|r|hの場合に有効)
my @count = qw( / .html .xml .rdf );
my %counter = ();
my $option = '-p';
if (@ARGV) {
    if ($ARGV[0] =~ /^-[prhPH]/) {
        $option = shift @ARGV;
    }
}
 
foreach (<STDIN>) {
    /^(\S+).+\[\S+ \S+\] "([^"]+)" (\d{3}) .+ "([^"]+)" "[^"]+"\n/;
    my $host = $1;
    my $request = $2;
    my $status = $3;
    my $referer = $4;
    my $key = '';
    my $out = 0;
    if ($option =~ /[prh]/) {        # -p|r|hの場合
        foreach (@count) {
            if (($request =~ /\S+ .*$_ \S+$/) &&
            ($status == 200)) {
                $out = 1;
                last;
            }
        }
    } elsif ($option =~ /[PH]/) {    # -P|Hの場合
        if ($status =~ /4\d\d/) {
            $out = 1;
        }
    }
    if ($out == 1) {
        if ($option =~ /[pP]/) {     # -p|Pの場合
            $request =~ /^\S+ (\S+) \S+$/;
            $key = $1;
        } elsif ($option =~ /r/) {   # -rの場合
            $key = $referer;
        } elsif ($option =~ /[hH]/) {    # -h|Hの場合
            $key = $host;
        }
        if (exists $counter{$key}) {
            $counter{$key}++;        # キーがあればカウントアップ
        } else {
            $counter{$key} = 1;      # キーがなければ作成
        }
    }
}
 
my @list = sort {                    # カウントの大きい順にソート
    $counter{$b} <=> $counter{$a} or
    $a cmp $b                        # カウントが同じ場合はキーでソート
} keys %counter;
foreach (@list) {                    # 出力
    my $star = '*' x int $counter{$_} / $counter{$list[0]} * 10 + 0.5;
    my $access = $_;
    if ($option =~ /[hH]/) {         # アクセス者の場合は後半40文字
        $access = substr($_, -40, 40);
    }
    printf "%10s %6d %-.40s\n", $star, $counter{$_}, $access;
}

 C'S SERVER Personalのログファイルは、日毎にzipで圧縮されているので、zcatの出力をパイプで入力すると便利です。

実行結果
7月のログファイルを入力してみました

 なお、このプログラムのソートでは、出現頻度が同じ場合はキーの文字列を先頭からソートしています。本来、ソートの対象がアクセス者(host)の場合は、ドット(.)区切りのワード毎に後ろからソートすべきだと思いますが、使用上問題がないので簡略化しています。(一言でいうと手抜きですね...(^_^;)

このページのトップに戻る