Filezillaログ解析

30分プログラム、その87。
FileZillaのログを解析して、最近更新されたやつのリストを出力する。

$ ruby ftp_recently.rb log/*
Recent Updates
============================================================
2007/07/21
-----------------------------------
- hoge.tar.gz

2007/07/19
-----------------------------------
- fizzbuzz.tar.gz


Ranking (Top 5)
============================================================
fizzbuzz.tar.gz               ***
hoge.tar.gz                   **
foo.jpg                       **
bar.png                       **
baz.zip                       **
#! /opt/local/bin/ruby -w
# -*- mode:ruby enconding:utf-8 -*-
#
# ftp_recently.rb -
#
# Copyright(C) 2007 by mzp
# Author: MIZUNO Hiroki <hiroki1124@gmail.com>
# http://mzp.sakura.ne.jp/
#
# Timestamp: 2007/07/22 09:50:14
#
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Ruby itself.
#

# Note: FTP Commands
#  - RETR filename  -  get this file
#  - STOR filename  -  put this file
#  - STOU           -  put this file and rename
#  - APPE filename  -  append/put this file
#  - RNFR filename  -  rename this file
#  - RNTO filename  -  rename to this name (following RNFR)
#
#  SEEALSO: http://www.atmarkit.co.jp/fnetwork/rensai/netpro11/ftp-command.html

# Note: Filezilla log format
#
#  (000635) 2007/07/21 21:35:23 - USERNAME (<IP>)> STOR FILENAME

require 'kconv'
require 'time'

Command = Struct.new('Command',:time,:command,:filename)

commands = []
ARGV.each{|filename|
  File.open(filename){|io|
    io.each{|line|
      if line =~ %r!(\d+/\d+/\d+\s+\d+:\d+:\d+) # 日付
                    .*
                    (RETR|STOR|STOU|APPE|RNTO) # 欲しいコマンド
                    \s+(.*)
                   !x then
        commands << Command.new(Time.parse($1),
                                $2.upcase.to_sym,
                                $3.chomp)
      end
    }
  }
}

class Time
  def same_day?(o)
    self.day == o.day and
      self.month == o.month and
      self.year  == o.year
  end
end

updates = commands.select{|x|
  [:STOR,:STOU,:APPE,:RNTO].include? x.command
}.sort{|a,b|
  b.time <=> a.time
}[0,20]

puts 'Recent Updates'
puts '='*60
time = nil
updates.each{|command|
  if time==nil or not command.time.same_day? time then
    puts '' if time
    puts command.time.strftime("%Y/%m/%d")
    puts '-'*35
    time = command.time
  end
  puts "- #{command.filename.toutf8}"
}

puts '','','Ranking (Top 5)','='*60
rank = Hash.new{|hash,key| hash[key] = 0}
commands.select{|x|
  if x.command = :RETR then
    rank[x.filename] += 1
  end
}

rank.to_a.sort{|a,b| b[1] <=> a[1]}[1,5].each{|filename,rank|
  span = ' ' * [30 - filename.size,0].max
  bar = '*' * rank
  puts "#{filename.toutf8}#{span}#{bar}"
}
  • ディレクトリ名がでないのが嫌だな
  • 汚ないコードだとは思います