シェルスクリプト上ではRVMはそのままでは実行できない
#!/usr/bin/env bash
set -eu
rvm use 2.7.2
デプロイを自動化しようとして例えばこのようなことをすると、次のようなエラーが起こり、実際にrubyのバージョンが指定できない。
RVM is not a function, selecting rubies with 'rvm use ...' will not work.
You need to change your terminal emulator preferences to allow login shell.
Sometimes it is required to use `/bin/bash --login` as the command.
Please visit https://rvm.io/integration/gnome-terminal/ for an example.
コンパイルエラーのような感覚でこのエラーを読むと「RVMは関数ではありません」と読めてしまい混乱する ("RVMは関数でないから決して関数みたいな呼び出し方はするな" という類のエラーに見える)が、このエラーはむしろ「RVMが関数ではありません」と読まれるべきものである。つまり、RVMはbin
フォルダに入っているもの(/usr/local/rvm/bin/rvm
など)を直接使うのではなく、同名のBash関数として定義されたものでなければ正しく機能しないらしい。
追記: 調べてみた
bin
というフォルダに入っているからといって、その中にある実行ファイルが binary
であるとは限らないようだ!(実質的な動作の違いはさほどないからだろうか?) /usr/local/rvm/bin/rvm
はテキストとして読めるBashスクリプトだった。ログインシェルの中でのみ、自動的に必要なファイルが読まれ、rvm
がshの関数に置き変わる。
# シェルスクリプトで type rvm を実行すると
$ cat ./test.sh
#!/usr/bin/env bash
type rvm
$ ./test.sh
rvm is /usr/local/rvm/bin/rvm
# しかしシェルプロンプトから直接実行すると
$ type rvm
rvm is a function
(関数が吐かれる)
一旦特定のファイルをsourceする必要がある
シェルスクリプトで使う場合にのみ、次のパスのどちらかにあるファイルを source
しないと動作しない。
source "$HOME/.rvm/scripts/rvm" # $HOMEにあるなら
source "/usr/local/rvm/scripts/rvm" # システムレベルであるなら
これを実行すると、 rvm
が関数に置き換えられて use
などのサブコマンドが使えるようになる。
それでも落ちる場合
set -eu
をしているとこの source
および rvm
コマンドは失敗する。
#!/usr/bin/env bash
set -eu
source "/usr/local/rvm/scripts/rvm"
rvm use 2.7.2
/usr/local/rvm/scripts/functions/support: 行 182: _system_name: 未割り当ての変数です (英語の場合"Unbound variable")
どうやら rvm
は未割り当て変数が存在する前提で動作するようになっているらしい。
そのため、 rvm
コマンドの操作が終わるまでは、 set +u
を明示的に指定した上でコマンドの実行が妨げられないようにしなければならない。
rvm
を実行し終わった後は set -eu
しても構わない。
期待通り動作するスクリプトの例
#!/usr/bin/env bash
set +u # WTF: rvmコマンド群の未定義変数エラーを回避
source "/usr/local/rvm/scripts/rvm"
rvm use 2.7.2
set -eu # エラー処理を元に戻す
bundle install
bundle exec something