Becky!2 → ThunderBird3 : mbox 構造調整スクリプト
Farewell to Becky!
長年使っているBecky!2がついに破綻して来た。
だいたい、機器の障害監視用のalert/notify メールはがんがん飛んでくるわ、お客さんは平気で数十Mbyteクラスのファイルを添付してMLにpost してくるわ、な環境では、Becky!のメッセージ管理のしくみでは、もう持たないのはBecky!のせいではない。
わたしのBecky!のフォルダは総計40GB以上、ひとつのフォルダに一万通以上のメッセージがあることもざらなのだから。
どうせ引っ越すなら、Windows とおさらばできるようにThunderBird 3 へ、ということでこの一週間、少しずつ引越しの準備を進めてきた。
なんとか簡単にできるようになったので、同じようなことを考えている人のために、移行方法のメモとスクリプトを残しておく。
How to export from Becky! and How to import to Thundirbird : research
ぐぐってみると意外にこの手の話題が少ない。
どちらも、CircleBeckyというBecky!のプラグインで、(たいていの人はそうであろう)階層構造化しているBecky!のメールフォルダとメッセージを再帰的にエクスポートしている。違いは、
- は、eml 形式でエクスポート(1メッセージ1ファイル)
- は、mbox 形式でエクスポート
しているところにある。
さらに 1. は、Thunderbird 側にImportExportToolsという拡張機能を入れて、フォルダ階層にエクスポートされた eml ファイルをフォルダ階層を再現しつつ取り込んでいる。
ひとことでいうと、
- は、操作はとても簡単で、上のリンクにあるとおりの手順で終わる。ただし、メッセージがたくさんある人は、エクスポートもインポートもとんでもなく時間がかかる。
- は、エクスポートが早く。インポートの操作はない(フォルダにほうり込むだけ)が、Becky!がエクスポートするmbox のフォルダ階層とThunderbird のそれが違うことから、フォルダ構造とパス名の調整が手作業で必要になる。
フォルダあたりに万単位のメッセージをかかえていた私には、1.の方法は中規模のフォルダてちょっと時間を計測してみて使い物にならない、とあきらめた。小規模ののテストフォルダなら、とても楽なんだが。
また、仕事がら一日数千のメールと戦うために数百に達しているフォルダの階層構造は自分でも覚ええていないくらいの深い深い密林と化しているので 2. の「手作業によるmboxの調整」も現実性がない。
というわけで、エクスポートがとても早い 2. の移行戦略を取り、「手作業」の部分を ruby にやらせることにした。
How to convert mboxes exported from Becky! into Thunderbirds' style
2. のmboxの変換の部分はとてもよくまとまっているので、ちょっと無断転載。
Thunderbirdではファイル自体がひとつのメールフォルダとして認識されてしまうので、CircleBeckyからの出力をそのまま置くと、[元のフォルダ名].mbox という名前のフォルダとして見えてしまう。さらに悪いことには、サブフォルダは[サブフォルダ名].sbd という名前のディレクトリを作っておかないと認識されないので、CircleBeckyの出力をそのまま置くと、サブフォルダはまったく認識されないということになってしまう。
という訳で、CircleBeckyから出力されたファイルの位置とファイル名、ディレクトリ名を変更しなければならない。手順は以下のとおり。
1. [メールフォルダ名].mbox という名前のファイルをすべて一つ上のディレクトリに移動する。
2. 空になったディレクトリはすべて削除する。
3. メールフォルダの名前が付いたディレクトリ、サブディレクトリの名前をすべて [元の名前].sbd に変更する。
4. [メールフォルダ名].mbox というファイルの名前から .mbox という拡張子を削除する。
実は、rayman さんが、まとめている手順はひとつ抜けている点がある。
MUAをつかっていると、複数のフォルダをかかえるためだけのメッセージを持たないフォルダができる。この場合がうまくいかない。
例えば、
customers
- customer_name1
- customer_name2
- customer_name3
という構成のBecky!フォルダがあり、customers には何もメッセージはなく、customer_name[1-3]には、実際の顧客のメッセージがある場合、まず Circle Becky は、以下のファイルをエクスポートする。
customers <-- directory
- customer_name1 <-- directory
- customer_name1.mbox <-- mbox file
- customer_name2 <-- directory
- customer_name2.mbox <-- mbox file
- customer_name3 <-- directory
- customer_name3.mbox <-- mbox file
これを上の手順で変換すると、
customers.sbd <-- directory
customer_name1 <-- mbox file
customer_name2 <-- mbox file
csutomer_name3 <-- mbox file
ができるが、これでは Thunderbird は、customer フォルダの存在を認識してくれない。同じディレクトリに、
customers
という mbox ファイル(空でよい)が必要になる。
上の手順にこの空のmbox を作る(touchする)ロジックを加えて、変換用のスクリプトを作成した。
ruby script for convert
以下は、変換にやっつけで作った、cvt.rb というスクリプト。
例によってテストもエラー処理もぜんぜんやってない(すみません。)
$KCODE = "SJIS" require 'jcode' require 'fileutils' def cvtB2mboxdir2thunderbird(t_dir) FileUtils.cd(t_dir) puts FileUtils.pwd subdir_found = false folder_contains_only_folders = true mbox_list = Array.new Dir.glob("*").each do |path| if /^(.+)\.mbox$/ =~ path FileUtils.mv(path, "../") mbox_list << path puts path if $1 == File.basename(t_dir) folder_contains_only_folders = false end end if FileTest.directory?(path) subdir_found = true cvtB2mboxdir2thunderbird(path) end end FileUtils.cd("..") puts FileUtils.pwd unless subdir_found FileUtils.rmdir(t_dir) else FileUtils.mv(t_dir, "#{t_dir}.sbd") end mbox_list.each do |mbox_file| basename = File.basename(mbox_file, ".mbox") FileUtils.mv(mbox_file, basename) end if folder_contains_only_folders FileUtils.touch(File.basename(t_dir)) end puts end cvtB2mboxdir2thunderbird(ARGV[0])
あっちこっちに、FileUtils.pwd を出力しているのは、どこまで変換が進んだかを見てないと退屈なのと、再帰的なロジックは昔から苦手なので、ようすをみたいから。
CircleBecky で mbox エクスポートしたフォルダをコマンドラインで指定して、
ruby -Ks cvt.rb C:\becky_exported\folder_top
で、ok。
あとは、変換された mbox ファイルとフォルダ群をThundirBird のデータフォルダに入れるだけ。
eml エクスポートの数十分の一の時間で移行できるので、仕事の合間に少しずつ、フォルダを移行できた。
このスクリプトを書いてみて、はじめて、FileUtils に touch なんてメソッドがあるのを知った。でも、FileUtils ってクラス名はいまいちいけてないなぁ、と思いません?
あとは、振り分けルールの変換かなぁ〜。変換するより、msgFilterRules.dat を簡単に生成するクラスでも書いておいて、コマンドラインから同形式のルールをばんばん作るほうが楽なような気がしてきたな。
Re: 【だいたいあってる時計】乱数の代わりに配列をつかってみたよ
kondoyuko さんのRuby修行のフォローです
id:kondoyuko:20110111:1294754578 で書かれているプログラムへのコメントです。
(はてなDiary のコメント内にはてな記法を使う方法がようわからんかった orz
作ろうとされている辻褄時計の目指しているものがいまいちつかめていない(というか経緯を知らない)ので、プログラムのお作法的なところでいくつかアドバイスを。
オブジェクト名と collect メソッド
ary = [] for i in 0..20 ary << 0.1 * i end
まず、ary っていうオブジェクト名(変数名)は、"配列"ということを意味しているんだろうけど、この場合は、"時計がどれだけ狂う(drift)するかの秒数のテーブル"ということのようだから、clock_drifts とでもしてみましょうか。
プログラムでは、オブジェクト名、メソッド名にわかりやすいものをつけて行く習慣がとてもだいじです。ima の方はとてもわかりやすいw
次に 0..20 という範囲オブジェクト(Rangeクラスのオブジェクト)は、Enumerableモジュールをインクルードしているので、配列オブジェクトと同じように collect という便利なイテレータメソッドが使えます。
以下、irb で示します。
irb(main):021:0> (0..5) => 0..5 irb(main):022:0> (0..5).class => Range irb(main):023:0> (0..5).to_a => [0, 1, 2, 3, 4, 5] irb(main):024:0> (0..5).collect{|i| i* 0.1 } => [0.0, 0.1, 0.2, 0.3, 0.4, 0.5] irb(main):025:0> a = (0..5).collect{|i| i* 0.1 } => [0.0, 0.1, 0.2, 0.3, 0.4, 0.5] irb(main):026:0> a => [0.0, 0.1, 0.2, 0.3, 0.4, 0.5] irb(main):027:0> a.class => Array
つまり、clock_drifts は、以下のように一行で定義できます。
clock_drifts = (0..20).collect{|i| i* 0.1 }
ちょっとクラスプログラミング風に
わたしが同じものを書くとたぶんこうなるだろうというものを。
loop 周りはちょっと動作を変えました start_ticking すると、
すぐに現在時刻を表示して無限ループに入ります。
class TsujitsumaClock CLOCK_DRIFTS = (0..20).collect{|i| i* 0.1 } def self.sleep_random sleep(CLOCK_DRIFTS.choice) end def self.start_ticking ima = Time.now loop do puts ima ima += 1 self.sleep_random end end end TsujitsumaClock.start_ticking
ちょっとしたロジックでも名前をつけてメソッドにする習慣をつけておくと、見通しがよくなり、テストや評価も楽です。
> 結果、よく解らん!!!あんまり変わんないような気がする。
と言うてはった、点も sleep_random というクラスメソッドの中だけを書き換えて、いろいろ統計を取ってみる・テストしてみればいいわけで。
で、統計を取って評価するプログラムも書いてみようかと思ったけど、もう、夜も遅いので今日はここまで。
2011年が連続した素数の合計であることはすごい!か?
2011年と素数
ツィッターで、2011年とは、次の11個の連続した素数の合計である。
2011 = 157 + 163 + 167 + 173 + 179 + 181 + 191 + 193 + 197 + 199 + 211
という話しを聞いた。
2011年は「セクシー素数」の年
http://wiredvision.jp/news/201101/2011010517.html
「すごい」は「美しい」か「めったにない」か
素数フリークな方には、「連続した素数の合計」というだけで、美しくてエクスタシーを感じてしまうものかもしれないが、一般人?な自分としては、どれだけ「めったにないか」、つまり「連続した素数の合計」である数(年号)は、AD1年から2011年までどれだけあったかをRubyで洗い出してみるか、ということに興味が出た。
要件定義
- 1 〜 2011 について調べる
- 二つ以上の連続した素数の合計である場合を選び出す
必要なもの
実装
めちゃくちゃべたべたやなあ〜。まあ、動けばいいや、という感じで。is_sum_of_contiguous_primes? は、テストを書いていないので、ちょとあやし〜。
class Fixnum def is_prime? return false if self == 1 2.upto(self -1) do |i| if self % i == 0 return false end end return true end def has_primes_lt_self primes = Array.new 2.upto(self) do |i| if i.is_prime? primes << i end end primes end def is_sum_of_contiguous_primes? primes = self.has_primes_lt_self while primes.size > 0 primes_for_sum = Array.new primes.each do |p| primes_for_sum << p sum = primes_for_sum.inject(:+) break if sum > self if sum == self && primes_for_sum.size > 1 return primes_for_sum end end primes.shift end return false end end
で、すごかったのか?
このメソッドを使って、以下のプログラムを書いた。タブ区切りで出力しているのは、リダイレクトした出力をExcel で眺めたから。
(2..2011).each do |year| if primes = year.is_sum_of_contiguous_primes? printf("%d\t", year) primes.each do |i| printf("%d\t", i) end puts("\n") end end
結果、2年から2011年の間に、連続した素数の合計であった年号は、790個あった。40%近い出現確率だから、めずらしいとは言えないかも。最近の20年では他に、1993, 1994, 1995, 1998, 1999, 2002, 2006 がある。
特に1998年は、
1998 = 31 + 37 + 41 + 43 + 47 + 53 + 59 + 61 + 67 + 71 + 73 + 79 + 83 + 89 + 97 + 101 + 103 + 107 + 109 + 113 + 127 + 131 + 137 + 139
と24個の連続した素数の和になっている。これはすごいなぁ〜(笑
Ruby で指定範囲の実数をランダムに返すメソッドを作る
kondoyuko さんとtwitter で表題の件を話していて、tweetでは説明しづらいので、こちらに。
def x_rand(from, to) return from.to_f + rand * (to.to_f - from.to_f) end
続いて、こんなプログラムを追加して、ばらつきぐあいを見てみた。
randomized_values = Array.new 1000.times do r = x_rand(-1, 1) puts r randomized_values << r end puts "-" * 20 puts randomized_values.inject(:+) puts randomized_values.inject(:+) / randomized_values.size puts randomized_values.max puts randomized_values.min
いかん、合計の計算に inject(:+) なんて使ってる(汗
初心者向きではないな〜
ま、いっか。