読者です 読者をやめる 読者になる 読者になる

ぱいぱいにっき

Pythonが好きすぎるけれど、今からPerlを好きになりますにっき

Perlを学んでいるとお得 テキストファイル解析編

どうもマコピーです。

こちらの記事は
Perl入学式 Advent Calendar 2014の13日目の記事です。

12日目は
猫のあずまさんこと@さんのプログラミング言語を学ぶという事についての私見でした。
なるほど興味深い。

今日の記事では「プログラミング言語を学ぶとして、そこでPerlってどうなのよ」みたいな側面からエモとテックを交えて語っていきたいと思います。

以下は個人の意見で僕の帰属している団体や会社とは関係ありません!!!

実用的便利ポイント

ログの解析

サーバの運用をやっているとテキストファイルにガーって数万行のファイルの特定の部分を見たい時があります。
Webサーバというのはアクセスログやデータベースのログが異常を検知するためや後からその時何が起こっていたかを調べたい時のために詳細なログが常にテキストファイルに流れています。
最近ではログファイル自体をデータベースに突っ込んで検索を容易にしている場合もありますが、それはおいといて。

で、アクセスログっていうのは以下の様なやつがいっぱいある感じになっているんですけれど、(これは今僕が個人的に作っているWebサービスの実験中のログです)

xx.xx.xx.xx - - [13/Dec/2014:07:19:56 +0900] "GET /static/bootstrap/js/bootstrap.min.js HTTP/1.1" 304 0 "http://example.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/xx.xx.xx.xx Safari/537.36"
xx.xx.xx.xx - - [13/Dec/2014:07:21:45 +0900] "GET / HTTP/1.1" 200 3736 "http://example.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/xx.xx.xx.xx Safari/537.36"
xx.xx.xx.xx - - [13/Dec/2014:07:21:45 +0900] "GET /static/bootstrap/css/bootstrap.min.css HTTP/1.1" 304 0 "http://example.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/xx.xx.xx.xx Safari/537.36"
xx.xx.xx.xx - - [13/Dec/2014:07:21:45 +0900] "GET /static/css/main.css HTTP/1.1" 304 0 "http://example.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/xx.xx.xx.xx Safari/537.36"
xx.xx.xx.xx - - [13/Dec/2014:07:21:45 +0900] "GET /static/bootstrap/js/bootstrap.min.js HTTP/1.1" 304 0 "http://example.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/xx.xx.xx.xx Safari/537.36"
xx.xx.xx.xx - - [13/Dec/2014:07:22:19 +0900] "GET / HTTP/1.1" 200 3736 "http://example.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/xx.xx.xx.xx Safari/537.36"

よく見たら1アクセス1行になっているようです。
で、例えばこの中からbootstrap.min.jsのアクセスだけ欲しいというときにどうすればいいでしょうか。
そうですね、Perlを使いましょう。

use strict;
use warnings;

my $count = 0;
while (my $line = <STDIN>) {
    if ($line =~ /bootstrap\.min\.js/) {
        $count++;
    }
}

print $count."\n";

以上の様なスクリプトを counter.pl と保存して、上記のログファイルを流し込みます。
catコマンドはファイルの中身を標準出力に流します。それを パイプ"|" でスクリプトに繋げることでスクリプトの標準入力にログファイルを流しこむことが出来ます。

$ cat access.log | perl counter.pl
32

出てきました。32回あったようです。
しかしここまでであれば実は他の言語(RubyPythonPHP)などでも出来る事です。

ここからがPerlっぽいところになります。ワンライナーを使いましょう。
ワンライナーとは.plファイルを作らずにコマンドの中に全部の処理を書いてしまうことです。

$ cat access.log | perl -e 'while (<STDIN>) { $c++ if $/bootstrap\.min\.js/; } print $c."\n";'
32

同じ結果が出てきました。ですが先程の.plに書いたスクリプトに比べたらだいぶダイエットというか……読めなくなってはいませんでしょうか?
例えば while() では my $line = が消えてしまっています。省略すると$_に1行が入ってくるのを利用しています。
また$cの初期化がされていませんが、初期化がされていない変数を++すると0とみなしてインクリメントを行うのを使っています。
さらに言えば正規表現の$_ =~ もなくなっていますが、これも省略可能です。
普段Perl入学式では「他の人にも読めるコードを心がけましょう」みたいなことをいうこともありますがワンライナーは対極にあります。
僕は使い捨てのコードなのでこんなかんじで圧縮して書くのですが、サクッと書けるメリットも有りよく使っています。

ちなみにですが以上の処理はPerlを使わなくてもコマンドを組み合わせると出来ます。
catに加えて、文字列で絞り込みをかけるgrepコマンドと標準入力の行数を数えるwcコマンドを使えばこんな感じです。

$ cat access.log | grep bootstrap.min.js | wc -l
32

でも複数コマンドを組み合わせるよりPerlで書いたほうが簡単に書けるようなケースも多々有りますし、そちらのほうが見やすいみたいなことがあります。
Perlにはgrepやmapもありますし、ハッシュや配列などのデータ構造も利用できます。それを組み合わせていろんな解析の仕方を実現できます。

もっと高度なPerlワンライナーは以下の記事が参考になります。
おそらくはそれさえも平凡な日々: awkの代わりにperlを使おう

ExcelというかCSV

「ログとかそういう職業の人だけじゃないの?」という疑問が浮かぶ方がいるかもしれませんが、「Perlでテキスト解析」するのは他のことにも使えます。
例えば現代では多くの方が使われているExcelではどうでしょうか。Excelに数字や項目をパチパチ打ち込んでいる方も多いかと思われます。
PerlExcelのファイルをそのままを扱うには一工夫必用なのですが、ExcelCSVファイルを作ればPerlでも簡単に扱えます。

"買った日", "品目", "数量", "買った場所"
"12/1", "ネギ", "2", "近所のスーパー"
"12/2", "サーボモータ", "1", "秋葉原"
"12/3", "ネギ", "3", "近所のスーパー"
"12/4", "みかん", "5", "近所のスーパー"
"12/5", "USBメモリ", "1", "ヨドバシカメラ"

みたいなCSVがあったとして、やたらネギ買ってるんですけれどネギ何本今まで買ったのかなみたいなときにワンライナーを書くと、

$ cat 買った物リスト.csv | perl -e 'while(<STDIN>) { @cell = split /,/; $c += $cell[2] =~ s/[" ]//gr if $cell[1] =~ /ネギ/; } print $c."\n";'
5

こんなかんじになります。しかしこれはちょっといろいろやらかしていますし、あと現実のExcelはこんなにきれいなCSVを吐いてはくれません。
さらに言えばセルの中にカンマが入ると容易に死ねます。そのときはText::CSVなどを使うと良いのですが、そのあたりのは話はPerl入学式でサポーターの方に聞いていただければと思います。

あとこの辺りの計算、Excelでやればええやんということもあるのですが、Excel普段起動していなくて重いので手元にあるCSVで手っ取り早くやるみたいなことを僕はやります。

エモい便利情報

エモというか主観情報を列挙しようかなと思ったんですけれど、思ったらそんなに「初心者にとって」というのはないかなと思いました。
というのもPerlやろうがRubyやろうがPythonやろうがPHPをやろうが、出来る事は大体一緒です。違うのは使っている人とコミュニティとモジュールぐらいでしょうか。
もしプログラムを仕事にしようとしている方でもどれかやっておけば他をやるときの移行コストはそこまで高くはありません。すくなくとも第一歩を踏み出すときよりはだいぶ低いです。
じゃあPerlを今始める理由は? と問われると「Perl入学式があるから」と答えたいです。だれでも参加できてかつ活発で本当に本当にカジュアルな勉強会は他の言語には見当たらないからです。
とはいえPerl入学しては見たけれど他の言語も試してみたいなという方はサポーターにご相談ください。たいていのサポーターはPerl以外にもやってます。というかPerl界隈の人はPerlだけの人はなかなかいないです。


というわけで「ほんとうに使えるのか!? Perl情報!」でした〜。
あと他のネタで「Perl入学式受講生に向けるサーバの選び方」みたいなのも書こうとしたんですけれど、ちょっとそれは置いときます。

明日は@さんの「モダンPerlを知ってびっくりしたこと書く予定」です。実はPerl入学式でやっているPerlはモダンなんですよ!楽しみですね!!!!