mbox形式のファイルを1メール1ファイルに分割する

またもや管理しているメールサーバでUnable to process From lines (envelopes), change recognition modes or check for corrupted mail drop.Commentsが起こったので、mbox形式のファイルを1メール1ファイルに分割するスクリプトを作った。ruby 2.1.0 で動作することを確認した。

以下を mbox2eml.rb として保存したとする。

# -*- coding: utf-8 -*-
require 'rubygems'
require 'mail' # https://github.com/mikel/mail/blob/master/README.md
require 'time'

class Mbox
  attr_reader :dirname

  def initialize(mboxFilename)
    if File.exist?(mboxFilename)
      @mboxFilename = mboxFilename
      @dirname = "#{@mboxFilename}_dir"
      @counter = 0
    else
      print "error: #{@mboxFilename} isn't in this directory!\n"
      exit
    end
  end

  def mkdirForEML
    Dir.mkdir(@dirname, 0755)
  end
  
  def divideToEML
    messageBuff = String.new
    File.open(@mboxFilename, "r") do | input |
      while line = input.gets
        line.scrub!('?')
        if (line.match(/\AFrom /))
          unless messageBuff.empty?
            @counter += 1
            File.open("#{@dirname}/#{@counter}", "w") do | output |
              output.puts messageBuff
            end
            messageBuff = ''
          end
        else
          messageBuff << line.sub(/^\>From/, 'From')
        end
      end
    end
  end

end

class EMLList

  def initialize(dirname)
    if File.exist?(dirname)
      @fileLists = Dir.glob("#{dirname}/[0-9]*")
      @fileLists.sort!{|a, b| File.basename(a).to_i <=> File.basename(b).to_i}
    else
      print "error: #{dirname} isn't in this directory!\n"
      exit
    end
  end

  def showInfo
     @fileLists.each{|filename|
      unless filename.nil?
        mail = Mail.read(filename) 
        puts "== #{filename} =="
        puts "From: #{mail.from.to_s}"

        subject = mail.subject.to_s
        if subject.empty?
          puts "Subject: == empty ==" 
        else
          puts "Subject: #{mail.subject.to_s}"
        end

        time = mail.date.to_s
        unless time.empty?
          puts "Time: #{Time.parse(time)}"
        else
          puts "Time: == can't get timestamp == "
        end
        puts "\n"
    end
  }
  end
end


begin
  if ARGV.length != 1
    puts "error: please input mbox filename.\n"
    exit
  end
  mboxFilename = ARGV[0]

  mbox = Mbox.new(mboxFilename)
  mbox.mkdirForEML
  mbox.divideToEML

  emls = EMLList.new(mbox.dirname)
  emls.showInfo
   
rescue => ex
  puts ex.message
end

このスクリプトrubygemsのmail ライブラリを使用している。なので、事前にインストールしておくこと。

% gem install mail

mbox形式のファイルを mbox.bak として保存していたとき、以下のようにして利用する。

% ruby mbox2eml.rb mbox.bak |& tee list.txt

mbox.bak_dir というディレクトリが作成され、その中に数字がファイル名のファイルが生成される。list.txt には、以下の情報がメールごとに表示される。「XX」は mbox.bak_dir 以下にあるファイル名(数字)。

  • == mbox.bak_dir/XX ==
  • From: 差出人メールアドレス
  • Subject: 件名
  • Time: 受信時刻