h3pei.tech
お仕事のご依頼
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/h3pei/.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 もコマンド実行と本来の実行可能ファイルの間に入り込んで、交通整理をしていますよね。

h3pei's icon

h3pei

フリーランスのソフトウェアエンジニア。Ruby / Rails アプリケーションの開発が得意領域。設計・実装・運用まで含めてプロダクト開発が好きです。

Questalという目標達成コミュニティサービスを開発しました。仲間と一緒に目標達成に取り組みたい方はぜひご利用ください。

お仕事のご依頼
GitHubnoteX

関連記事