LTSVログをパースする最強のワンライナー集

最初に結論

これ最強
cat accesslog | perl -F'\t' -nale '%h=map{split/:/,$_,2}@F;print"$h{time}\t$h{ua}"'

なぜPerlワンライナーなのか?

LTSVログを解析するには、Perlのワンライナーに限る。
なぜならPerlはほとんどのサーバにデフォルトで入ってるから。
Perl 5.8で動くワンライナーを覚えておけばどの環境でも使える。

「何にも依存しない」
これ最強。

基本中のキホン

まずは基本形です。
これだけは絶対覚えてしまいましょう。
perl -nale 'print'
% cat access_log | perl -nale 'print '
time:[22/Feb/2014:15:13:07 +0900]       host:10.10.200.102      ident:- user:-  method:GET      path:/css/user.css    query:?20130919 proto:HTTP/1.1       status:200      size:14164      referer:https://example.com/home        ua:Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko      taken:199
これは何もしないフィルタで、ただ cat してるのと同じことです。
しかし、これさえ覚えれば勝ちです。

え?右側の -nale 'print'が覚えられないって?
「ネイルプリント」とでも覚えておきましょう。

こんな感じで。
ネイルプリント
nailprint.jpg

タブ区切りを配列として格納

perl -F'\t' -nale 'print @F'
出力はさっきとほぼ同じです。
% cat access_log | perl -F'\t' -nale 'print @F'
time:[22/Feb/2014:15:13:07 +0900]host:10.10.200.102ident:-user:-method:GETpath:/css/user.cssquery:?20130919proto:HTTP/1.1status:200size:14164referer:https://example.com/homeua:Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Geckotaken:199
time:[22/Feb/2014:15:13:07 +0900]host:10.10.200.102ident:-user:-method:GETpath:/images/user.gifquery:proto:HTTP/1.1status:200size:577referer:https://example.com/homeua:Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Geckotaken:145
ここまでだとあまり実用性はないです。
面白いのはここからだ・・・!!

行列の縦横変換

だんだん実用的になってきます。
perl -F'\t' -nale 'print for @F'
% cat access_log | perl -F'\t' -nale 'print for @F'
time:[22/Feb/2014:15:13:07 +0900]
host:10.10.200.102
ident:-
user:-
method:GET
path:/css/user.css
query:?20130919
proto:HTTP/1.1
status:200
size:14164
referer:https://example.com/home
ua:Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
taken:199
time:[22/Feb/2014:15:13:07 +0900]
host:10.10.200.102
ident:-
user:-
method:GET
path:/images/user.gif
だいぶ見やすくなりました。
でも1リクエストごとの切れ目がないのでちょっと見にくいですね。
区切りを入れてみましょう。

1リクエストごとに空行を入れる

perl -F'\t' -nale 'print for @F;print ""'
% cat access_log | perl -F'\t' -nale 'print for @F;print ""'
time:[22/Feb/2014:15:13:07 +0900]
host:10.10.200.102
ident:-
user:-
method:GET
path:/css/user.css
query:?20130919
proto:HTTP/1.1
status:200
size:14164
referer:https://example.com/home
ua:Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
taken:199

time:[22/Feb/2014:15:13:07 +0900]
host:10.10.200.102
ident:-
user:-
だいぶ見やすくなりました!
急いでるときはこんなんでも十分でしょう。

行をハッシュに変換する

LTSVのメリットはハッシュと相性がよいことです。
PerlでLTSVの行をハッシュに変換するには次のようにします。
perl -F'\t' -nale '%h=map{split/:/,$_,2}@F;print"$h{time}\t$h{ua}"'
% cat access_log | perl -F'\t' -nale '%h=map{split/:/,$_,2}@F;print"$h{time}\t$h{ua}"'
[22/Feb/2014:15:13:07 +0900]    Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
[22/Feb/2014:15:13:07 +0900]    Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
[22/Feb/2014:15:13:07 +0900]    Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
見やすい!

Data::Dumperモジュールを使ってハッシュを丸ごと表示してみるのもよいでしょう。
perl -MData::Dumper -F'\t' -nale '%h=map{split/:/,$_,2}@F;print Dumper \%h'
cat access_log | perl -MData::Dumper -F'\t' -nale '%h=map{split/:/,$_,2}@F;print Dumper \%h'
$VAR1 = {
          'ua' => 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko',
          'proto' => 'HTTP/1.1',
          'status' => '200',
          'query' => '',
          'time' => '[22/Feb/2014:15:13:07 +0900]',
          'path' => '/images/user.gif',
          'ident' => '-',
          'size' => '149',
          'host' => '10.10.200.102',
          'taken' => '127',
          'user' => '-',
          'method' => 'GET',
          'referer' => 'https://example.com/home'
        };

$VAR1 = {
          'ua' => 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko',
          'proto' => 'HTTP/1.1',
べんり!

おまけ:ログファイルは必ずパイプで渡す

さてここまで読んで、「わざわざ左でcatコマンド起動するの馬鹿じゃね?」と思われたかもしれません。その通りです。
catするぐらいなら、ファイル名を右に書く、つまり標準引数や入力リダイレクトを使う方が、速度は速いでしょう。
perl -nale 'print' access_log
または
perl -nale 'print' < access_log
しかし左側にcatをわざと書いたのには理由があります。
cat access_log | perl ...
この形を覚えておけば、catのかわりにtail,head,grepなどを使うときに楽なのです。
# リアルタイム表示
tailf accesslog | perl ...

# 行絞り込み
grep path:/images/ accesslog | perl ...
 
# gz圧縮されたログを展開
gunzip -c access_log.1.gz | perl ...

# 複数の圧縮ログを展開
gunzip -c access_log.*.gz | perl ...
それではHappy parsing!

参考

カテゴリ: