macのcronでRuby2.0を使用したアプリケーションでincompatible character encodingsが発生した

ruby

Ruby2.0がリリースされて久しいですが、仕事が忙しく全くコードを書く時間が取れませんでした。今回台風というありがたくはないことですが、そのおかげでお休みになった今日を利用して簡単なバックアップアプリケーションを作成しました。

内容はとても単純で、経理用に使用したりJenkins用に稼働しているVMWareの仮想ディスクをpauseしてバックアップを作成して再実行するというものです。特に経理用のWindowsは環境のバックアップがとても重要ですのでこの機会にきっちりバックアップを仕組みとして起きたいと考えていました。

プログラム自体は単純で、

  1. systemコマンドを使用して起動している仮想マシンの停止
  2. ディレクトリ単位でのバックアップ
  3. 停止した仮想マシンの再開
  4. 処理結果をメールで送信

という流れになります。

作成したプログラムはコンソールから実行することができ、普通に実行するとメールで正常終了の通知が送信されてきます。とりあえず目的は達成できたということで、cronを使用して定時実行させようとしたところ、プログラムを書いた時間の倍ほどの時間はまりました。

結論から言うと、幾つかのことが正しく設定されていませんでした。

  1. cronの実行環境とログイン時の実行環境が異なる事を理解していなかったため、rbenvなどのパスが正しく設定されていなかった。(path問題 => crontabにパスを通した)
  2. cron実行時のlocaleとログイン時のlocaleが異なる事を理解していなかったため、実行時にエンコード関連のエラーが発生した。(locale問題 => ~/.bash_profile に ロケール設定を追加した)

1つめは純粋にRubyが実行できない状態でした。今回の環境はmac mini で動作する OS X Serverだったのですが、通常のLinuxとそれほど変わらないという認識でいました。実際にGUIのしっかりしたLinuxという感覚で使用していたのですが、ネット上の情報量の少なさは結構つらかったです。

Rubyをcronから実行する場合については

PATH=/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/$HOME/bin:$PATH

こちらを参考にしました。特にcronでrubyバージョンを出力すると言うことは考えても居なかったのですが、確実に環境を確認する方法がわかりました。

2つ目は悩みました。1つ目はパスの問題で、大なり小なりどこかで遭遇するタイプの問題でした。ところが、2つ目については実行するコンテキストで同一のプログラムがエンコードに起因したエラーを発生したのです。

具体的なエラー内容は下記の通りです。

[bash] incompatible character encodings: UTF-8 and ASCII-8BIT [/bash]

ここでまず最初に感じた事は、Ruby2からエンコードはUTF8になったはずなのに??ということでした。この事もあって何何やらさっぱりわからない状態になりました。

とはいうものの、エンコードが正しくないと言うことはわかっていたので調べてみると、こちらのサイトが参考になりました。

rvmのcronでLANGが設定されていない場合の対応 - rochefort's blog

今回使用していたのはrbenvで少し違いましたが、LANGが正しく指定されていないことによりエンコード関連のエラーが出る事がわかりました。ということで早速調べてみたところ、localeが以下の通りとなっていました。

[bash]

execute.sh

locale

実行結果

$ ./execute.sh LANG= LC_COLLATE="C" LC_CTYPE="C" LC_MESSAGES="C" LC_MONETARY="C" LC_NUMERIC="C" LC_TIME="C" LC_ALL= [/bash]

この結果から若干異なるわけですが、似たような事象である事を確信しました。

ちなみに、普通にログインしている状態でターミナルからlocaleコマンドを実行すると以下の通りとなりました。

[bash] $ locale LANG="ja_JP.UTF-8" LC_COLLATE="ja_JP.UTF-8" LC_CTYPE="ja_JP.UTF-8" LC_MESSAGES="ja_JP.UTF-8" LC_MONETARY="ja_JP.UTF-8" LC_NUMERIC="ja_JP.UTF-8" LC_TIME="ja_JP.UTF-8" LC_ALL= [/bash]

ログインして普通に実行する場合には正常に動作しているのでそこを目指せばよいことはわかりました。そのためにいろいろと試したのですが、どうしてもLANG以外は変更できませんでした。

最終的に、どうにもわからないまま .bash_profile に以下の一文を追加したところ正常にlocaleが設定されました。

[bash]

.bash_profile

export LC_ALL='ja_JP.UTF-8' [/bash]

追記してから

[bash] source ~/.bash_profile [/bash]

を実行してみたところ、

[bash] $ ./execute.sh LANG="ja_JP.utf8" LC_COLLATE="ja_JP.UTF-8" LC_CTYPE="ja_JP.UTF-8" LC_MESSAGES="ja_JP.UTF-8" LC_MONETARY="ja_JP.UTF-8" LC_NUMERIC="ja_JP.UTF-8" LC_TIME="ja_JP.UTF-8" LC_ALL="ja_JP.UTF-8" [/bash]

となり正常にアプリケーションを実行できるようになりました。

Linux環境やmacは十分なれていないことはわかっていましたが、いろいろと試してみる必要がありそうです。