まさはる's Weblog

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

2004年7月24日

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

Category : Terminal / Posted at 2004年7月24日 16:49

 その後、Getopt::Stdモジュールを利用して、いくつかの便利そうなオプションを追加しました。

ヘルプの出力
hオプションでヘルプを出力

実行結果
sオプションでユーザーエージェントに「Mac」のある行を出力してみました
#!/usr/bin/perl -w
use strict;
use Getopt::Std;
# ログファイルの整理プログラム log.pl ver. 1.3
 
# 入力するログファイルのフォーマット
# 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|r|h|uの場合に有効)
my @count = qw( / .html .xml .rdf );
my %counter = ();
my %opt = ();
 
# オプション設定
getopts ('pruaPUs:S:c:l:h', \%opt);
if (! $opt{p} && ! $opt{r} && ! $opt{u} && ! $opt{a} &&
    ! $opt{P} && ! $opt{U}) {
    $opt{p} = 1;                        # 出力の標準
}
if (! $opt{c} || $opt{c} < 20 || $opt{c} > 256) {
    $opt{c} = 79;                       # 出力桁の標準値
}
if (! $opt{l} || $opt{l} < 1) {
    $opt{l} = -1;                       # 出力行の標準値
}
if ($opt{s}) {
    print "-s search pattern: ", $opt{s}, "\n";
}
if ($opt{S}) {
    print "-S search pattern: ", $opt{S}, "\n";
}
if ($opt{h}) {
    print "ログファイルの整理プログラム ver. 1.3\n" .
    "log.pl [-p|r|u|a|P|U]\n" .
    "       [-s search_pattern] [-S search_pattern]\n" .
    "       [-c output_columns] [-l output_lines] [-h]\n\n" .
    "出力オプション\n" .
    "p アクセス先を回数でソート(passの前半を出力)\n" .
    "  オプション指定が無い場合の出力の標準\n" .
    "r アクセス元を回数でソート(refererの前半を出力)\n" .
    "u アクセス者を回数でソート(hostの後半を出力)\n" .
    "a ブラウザ  を回数でソート(user-agentの前半を出力)\n" .
    "P クライアントエラーを出したアクセス先を回数でソート\n" .
    "U クライアントエラーを出したアクセス者を回数でソート\n" .
    "s search_pattern 正規表現のパターンにマッチする場合出力\n" .
    "S search_pattern 正規表現のパターンにマッチしない場合出力\n" .
    "c output_columns 指定桁まで出力(値は20..256,標準は79桁出力)\n" .
    "l output_lines   指定行まで出力(値は1以上,  標準は全て出力)\n" .
    "h ヘルプを出力\n";
    exit 0;
}
 
# 標準入力からのログファイルの読み込み
foreach (<STDIN>) {
    my($host, $request, $status, $referer, $agent) =
    /^(\S+).+\[\S+ \S+\] "([^"]+)" (\d{3}) .+ "([^"]+)" "([^"]+)"\n/;
 
    # 該当行の抽出
    my $out = 0;
    if ($opt{p} || $opt{r} || $opt{u} || $opt{a}) { # -p|r|u|aの場合
        foreach (@count) {
            # パスの末尾が合致し、かつstatusが200番の行を抽出
            if (($request =~ /\S+ .*$_ \S+$/) && ($status == 200)) {
                $out = 1;
                last;
            }
        }
    } elsif ($opt{P} || $opt{U}) {      # -P|Uの場合
        if ($status =~ /4\d\d/) {       # statusが400番台の行を抽出
            $out = 1;
        }
    }
 
    # ハッシュに該当行を格納
    if ($out == 1) {
        my $key ="";
        if ($opt{p} || $opt{P}) {       # -p|Pの場合
            $request =~ /^\S+ (\S+) \S+$/;
            $key = $1;
        } elsif ($opt{r}) {             # -rの場合
            $key = $referer;
        } elsif ($opt{u} || $opt{U}) {  # -u|Uの場合
            $key = $host;
        } elsif ($opt{a}) {             # -aの場合
            $key = $agent;
        }
        if ((! $opt{s} && ! $opt{S}) || # -s|Sオプションなし
        ($opt{s} && ! $opt{S} && $key =~ /$opt{s}/) || # -sの時
        ($opt{S} && ! $opt{s} && $key !~ /$opt{S}/) || # -Sの時
        ($opt{s} && $opt{S} &&          # -s-Sの時
        $key =~ /$opt{s}/ && $key !~ /$opt{S}/)) {
            if (exists $counter{$key}) {
                $counter{$key}++;       # キーがあればカウントアップ
            } else {
                $counter{$key} = 1;     # キーがなければ作成
            }
        }
    }
}
 
# 標準出力への書き出し
if (keys %counter > 0) {                # %counter が空でなければ表示処理
 
    # カウントの大きい順にソート
    my @list = sort {
        $counter{$b} <=> $counter{$a} or
        $a cmp $b                       # カウントが同じ場合はキーでソート
    } keys %counter;
    if ($opt{l} != -1 && $opt{l} < @list) {
        for (1..@list - $opt{l}) {      # 不要な行を削除
            pop @list;
        }
    }
 
    # 出力
    my $c_len = length $counter{$list[0]};  # カウントの文字数
    my $a_len = $opt{c} - $c_len - 12;  # キーの文字数
    foreach (@list) {
        my $star = "*" x int $counter{$_} / $counter{$list[0]} *
        10 + 0.5;
        my $access = $_;
        if ($opt{u} || $opt{U}) {       # -u|Uの場合は後半を出力
            $access = substr($_, -$a_len, $a_len);
        }
        printf "%10s %" . $c_len . "d %-." . $a_len . "s\n",
            $star, $counter{$_}, $access;
    }
}

 -sと-Sオプションの引数は正規表現で入力するのですが、上記プログラムは少し問題があります。例えば、「ne.jp」の正規表現は、メタキャラクタの「.」をにバックスラッシュを付けて、ne\.jpとすれば良いのですが、このプログラムの場合はオプションを%optに代入するまでの間にバックスラッシュが1つ使われてしまうようで、ne\\.jpとしないと正常に動作しません。どこで使われてしまうのかな...(?_?;

 ふと、正規表現の文字列を「"」や「'」で括ってみたところ、正常に動作するようになりました。なーんだ...(^_^;

ロボット?
Mozilla/x.0を含む行を除くと、ロボットっぽいものばかりに...
このページのトップに戻る