2021-07-12

ちゃんと理解するrbenv : (4) shimsを理解する

はじめに

本記事は、「ちゃんと理解するrbenv」の第4回です。
前回の記事

今回は、rbenvにおける重要な概念である shims について整理します。

shimsとは

shimsは rakerubocoprspec などといったRubyのコマンド単位で生成される実行可能なシェルスクリプトです。rbenvによって作られています。

shimsは通常、~/.rbenv/shims 以下に配置されています。筆者の環境の ~/.rbenv/shims は次のようになっていました。

$ ls ~/.rbenv/shims
bundle           htmldiff         nokogiri         rbs              rubocop          solargraph       y2racc
bundler          irb              racc             rdoc             ruby             thor             yard
erb              kramdown         racc2y           reverse_markdown ruby-parse       tilt             yardoc
gem              ldiff            rake             ri               ruby-rewrite     typeprof         yri

shimsには全てのバージョンのRubyの実行可能コマンドが配置されます。
例えば 2.7.3 でrubocopをインストールし、3.0.1 でrspecをインストールした場合、shimsディレクトリにはrubocopとrspecの両方が配置されることになります。

shimsの役割

rubocop を例にして説明します。

通常、rubocop コマンドを実行するとrubocopというgemが提供する実行可能ファイルが実行されます。
rbenvを使っているとその実行可能ファイルは ~/.rbenv/versions/X.X.X/bin/rubocop に配置されています (X.X.X の部分はバージョン)。

しかし、rbenvを使っている環境においてはshimsに配置された ~/.rbenv/shims/rubocop が最初に呼び出されます。

こうして実行されたshimが何をしているかというと、

  • 現在のRubyのバージョンを特定する
  • 特定したバージョンのRubyの本来の実行可能ファイルである rubocop コマンドを実行する

ということをしています。

shimを経由してコマンドが実行される
shimを経由してコマンドが実行される

上の図のように、複数のバージョンのRubyがありその全てで rubocop をインストールしている場合でも、rbenvのshimを介すことで適切なバージョンのRubyを選択し、その先の rubocop コマンドにディスパッチすることが可能になります。

環境変数PATHとshims

これまで整理してきたようにshimsを機能させるには、Rubyのコマンドが呼びされたとき 必ず最初にshimが実行される ようにしないといけません。rbenvはこれをどのようにして実現しているでしょうか?

第2回 基本的な使い方 のときにrbenvの初期設定として、.zshrcファイルに eval "$(rbenv init -)" という設定を追記したことを覚えているでしょうか。実はこの rbenv init を実行することで、環境変数PATHの先頭に ~/.rbenv/shims を追加するということが裏で行われています。

以下は筆者の環境におけるPATH環境変数の値です。PATHの先頭にshimsへのパスがあることがわかります。

$ echo $PATH
/Users/mogulla3/.rbenv/shims:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:...(省略)

環境変数PATHは、rubyに限らずシェル上で実行されたコマンドを探すときのパスの一覧が : 区切りで記述されています。このとき前にあるパスほど優先されます。 よって、一番先頭にあるshims以下に存在すれば必ず最初に実行させることができる、という仕組みになっているのです。

rbenvはいつshimsを作るのか

ところで、shimsディレクトリ以下のスクリプトはいつどのようにして作られるでしょうか?これも当然rbenvがやっているわけですが、これはrbenvの rehash という処理が行われたタイミングで作られています。

そしてこのrehash処理も rbenv init により呼び出されています。

rehashの詳細については rbenv rehashをちゃんと理解する という過去の記事に書いているので、興味のある方は参考にしてください。

まとめ

本記事ではshimsを中心にして関連する概念の整理をしました。

  • shimsは、Rubyのコマンド単位で生成される実行可能なシェルスクリプト。通常 ~/.rbenv/shims 以下に配置される。
  • Rubyのコマンドを実行すると、最初にshims以下のスクリプトが実行される。その後にRubyバージョンを特定した上で、shimから本来のコマンドが呼び出される
  • rbenv init はshimsのパスを環境変数PATHの先頭に追加するということと、 rbenv rehash によりshimを生成する仕事をしていた

余談ですが、shimという単語には「部品の間にはさんで水平にしたり高さや位置を調整する板」とか「詰め木」という意味があるようです。最初、筆者はshimという見慣れない単語を見て ? となりましたが、このように役割を理解するとピンと来る名前だなぁと感動しました。rbenvのshimもコマンド実行と本来の実行可能ファイルの間に入り込んで、交通整理をしていますよね。