Rubyから別のRubyプロセスをsystem関数などを使って起動する方法をまとめておく。
例えば、CRubyからJRubyなど別のバージョンのRubyを呼びたい場合に使える。
ここではrubyのバージョン管理にrbenvを使っているものとする。
まず親プロセスから呼ぶときには、シェルスクリプトを作ってその中でrubyのバージョンを切り替えるようにする。
rbenvのコマンドを使ってrubyのバージョンを切り替える。
ここではサンプルとしてCRubyからJRubyを呼ぶケースを考えよう。
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
rbenv shell 'jruby-1.7.13'
ruby --version # => jruby 1.7.13 ....
最初の2行はrbenvの初期化処理。
rbenv shell
というのはそのシェルの中でのみrubyのバージョンを切り替えるコマンド。
このコマンドを呼び出すために、親プロセスの方でsystemで呼ぶ。
system("sub.sh")
これでうまくいくように思えるのだが、親のプロセスを bundle exec
で実行している場合に問題になることがある。
bundle exec
で実行すると、RUBYLIB, RUBYOPT, GEM_HOME, BUNDLE_BIN_PATH, BUNDLE_GEMFILE といった環境変数がセットされる。
環境変数は起動した子プロセスにも持ち越されるため、jrubyを起動していてもcrubyのgemのパスを検索しに行ってしまう。
これを防ぐには親プロセス側で、以下のように呼ぶ。
Bundle.with_clean_env do
system("sub.sh")
end
こうすると、BundlerでセットされたBUNDLE_...環境変数がアンセットされる。
しかしRUBYLIB, GEM_HOME だけはアンセットされない。(なんで??)
ということでsystemで呼ぶ時にRUBYLIBとGEM_HOMEの環境変数をunsetする。
Bundle.with_clean_env do
system({'RUBYLIB'=>nil, 'GEM_HOME'=>nil}, "sub.sh")
end
(systemの第一引数にコマンド実行時にセットしたい環境変数をHashで渡すことができます。)