簡単に言うと、rbenv の shims はシェルの `PATH` 探索とは独立して実行ファイルを探索しているため。
以下、単に rbenv としているけれどクローンである plenv なども同様と考えてよい。
shims とは
rbenv の README が詳しい。
Shims are lightweight executables that simply pass your command along to rbenv. So with rbenv installed, when you run, say, rake, your operating system will do the following:
GitHub - rbenv/rbenv: Groom your app’s Ruby environment
- Search your PATH for an executable file named rake
- Find the rbenv shim named rake at the beginning of your PATH
- Run the shim named rake, which in turn passes the command along to rbenv
シェルは shims だけを探索する。
shims を実行すると rbenv によって Ruby のバージョンごとの実行ファイルが探索され、見つかった実行ファイルを `exec` する。
セットアップスクリプトの例
アプリケーションのセットアップを自動化するために以下のようなスクリプトが添えられていることが多いと思う:
#!/bin/bash set -e which bundle || gem install bundler bundle install
Bundler がなかったら入れて、 `bundle install` で依存ライブラリを入れて終わり。
`which(1)` が成功するけど `rbenv which` が失敗する例
そんなことがあるのかと思うけれど、shims のライフサイクルを考えるとありうる。
rbenv で管理している Ruby のいずれかで Bundler をインストールした時点で `bundle(1)` の shim が作られる。
一方で、各バージョンごとの実行ファイルの実体は、各バージョンにおいてインストール (gem install) しないと存在しない。
つまり以下のようになる:
rbenv shell 2.0.0 rbenv exec gem install bundler which bundle # => ~/.rbenv/shims/bundle: この時点で shim が作られる rbenv which bundle # => ~/.rbenv/versions/2.0.0/bin/bundle: 実体も作られる rbenv shell 2.4.0 which bundle # => ~/.rbenv/shims/bundle: 既に 2.0.0 でインストールし shim は作られている rbenv which bundle # => (fail): 2.4.0 ではまだインストールしていないので存在しない! rbenv exec gem install bundler which bundle # => ~/.rbenv/shims/bundle: 既に 2.0.0 でインストールし shim は作られている rbenv which bundle # => ~/.rbenv/versions/2.4.0/bin/bundle: めでたく 2.4.0 に対応する実体もインストールされた
先に紹介したセットアップスクリプトは `which bundle` で shim が探索されて見つかってしまうので `gem install bundler` が実行されないが、そのアプリケーションで使うバージョンに対応する実行ファイルの実体は存在しないので `bundle install` が失敗する、ということが起きてしまう。
そこで rbenv exec/rbenv which
どうすればいいかというと、タイトルにあるとおり `rbenv exec` / `rbenv which` を使うとよい。
これらは基本的に `exec(1)` `which(1)` の shims を考慮するバージョンと考えてよいので単純にコマンドに前置するだけで対応はほぼ完了する。
まとめ
- rbenv ないし rbenv のクローンを使っているときは `rbenv exec` `rbenv which` を使う