#この記事で取り扱う内容
無邪気にRuby on Railsを使おうとすると以下のようなメッセージが出ることがあります。
$ rails --version
Rails is not currently installed on this system. To get the latest version, simply type:
$ sudo gem install rails
You can then rerun your "rails" command.
メッセージに従って sudo gem install rails
を実行しても同じメッセージが出るばかりです。困りました。
#想定する読者
この記事はそんな、何回Ruby on Railsをインストールしても Rails is not currently installed on this system.
と言われて困ってしまった人のために書きました。私のことです。
開発環境構築は初心者には難しいことが多く、せっかくWebの世界に入って何か作ってやろうという意気込みが初っ端からくじかれてしまいます。
しかし、あなたがこの記事にたどり着いたということは、問題が発生したら調べる(または人に聞く)という基本動作ができているということです。おめでとうございます! あなたは一人前のソフトウェアエンジニアを名乗っても大丈夫です!
この記事ではそんな駆け出しのエンジニアが対象です。
もしわからない部分がある場合は私の書き方が悪いのでお気軽に質問してください。きっと私もよくわかっていないので一緒に悩みましょう。
ここ間違ってるよ等の指摘も大歓迎です。よろしくお願いします。
#tl;dr
どうでも良いから早く解決したいという人はこれやってみてください。
hash -r
#環境
次のような環境で実施しています。
この章は読み飛ばして構いません。
項目 | バージョン |
---|---|
OS | MacOSX HighSierra |
シェル | bash v3.2 |
ruby | v2.5.1 (brewでinstall) |
gem | v2.7.6 (brewでrubyと一緒にinstall) |
rails | 未install |
rbenv | 未install |
##環境の確認
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.13.3
BuildVersion: 17D47
$ echo $SHELL
/bin/bash
$ /bin/bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17)
Copyright (C) 2007 Free Software Foundation, Inc.
$ ruby --version
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]
$ gem --version
2.7.6
rbenvは利用していません
$ rbenv
-bash: rbenv: command not found
PATHは次の通りです
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
#状況の再現
まずは、状況を確かめます。
再度確認しておくと、目標はmacにRuby on Railsをインストールして rails --version
でバージョン情報が表示されることです。
それではバージョンを表示させましょう。
$ rails --version
Rails is not currently installed on this system. To get the latest version, simply type:
$ sudo gem install rails
You can then rerun your "rails" command.
どうやら失敗したようです。
「Railsは現在システムにインストールされていません、sudo gem install rails
というコマンドで最新版のRailsをインストールすることができます。」
と言われてしまったので、素直に従います。
$ sudo gem install rails
Fetching: concurrent-ruby-1.1.3.gem (100%)
Successfully installed concurrent-ruby-1.1.3
.
(省略)
.
Fetching: rails-5.2.1.gem (100%)
Successfully installed rails-5.2.1
38 gems installed
インストールされました。
それでは満を持して rails
コマンドを叩きましょう!
$ rails --version
Rails is not currently installed on this system. To get the latest version, simply type:
$ sudo gem install rails
You can then rerun your "rails" command.
🤔🤔🤔🤔🤔
インストールされていないように見えます。念のためもう一回 sudo gem install rails
を実行しても同じ結果になります。困りました。
調査
焦らず一つ一つ順番に確認していきます。
##railsコマンドの実行ファイルの確認
which
というコマンドで、railsコマンドの実行ファイルの場所を確認します。
$ which rails
/usr/local/bin/rails
/usr/local/bin/rails
が実行ファイルのようなので、絶対Pathでコマンドを叩いてみます
$ /usr/local/bin/rails --version
Rails 5.2.1
🤔🤔🤔🤔🤔
実行できました。おめでとうございます。解散!…というわけにはいきません。 rails
コマンドは依然通らないままです。railsコマンドと/usr/local/bin/rails
は別物なのでしょうか?
ひとまず実行できるRuby on Railsがインストールされていることを確認したところで調査を次の段階に進めます。
##railsコマンドの探索Path
rails
コマンドが/usr/local/bin/rails
と同じかどうかを調べるためにはrails
コマンドが実行された時の挙動を知る必要があります。
シェル(ここではbash)でコマンドを打ったとき、曖昧な知識を頼りに大雑把に言えばシェルはPATHという変数に記録されている場所からコマンドに合致する実行ファイルを探して実行します。
(注)細かいことや正しいことが気になる方はドキュメントを参照してください。
PATHの確認方法は以下です。
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
PATH変数にはコロン(:)区切りで実行ファイルが置いてあるフォルダが羅列されています。優先順位は左から優先です。
例えば、 rails
というコマンドを打ったら、シェルは /usr/local/bin
の中を確認して rails という名前の実行ファイルがあれば実行します。なければ/usr/bin
を探し、rails があれば実行します。なければ、PATHの右端まで探索を続けます。もし最後まで実行ファイルを見つけることができなかった場合、 command not found
のようなメッセージが表示されます。
$ hoge
-bash: hoge: command not found
今回の問題では rails: command not found
とは表示されていないため、 railsコマンドはどこかに存在することがわかります。
次に、次の場所が上から順番に探索されることがPATH変数からわかります。
- /usr/local/bin
- /usr/bin
- /bin
- /usr/sbin
- /sbin
それでは、どこにrailsコマンドがあるかを確認しましょう。
ls
で確認しても良いですし
$ for path in $(echo $PATH | sed -e 's/:/ /g'); do echo "$path -> $(ls $path | grep rails)"; done
/usr/local/bin -> rails
/usr/bin -> rails
/bin ->
/usr/sbin ->
/sbin ->
which -a
とかtype -a
でも良いです
$ which -a rails
/usr/local/bin/rails
/usr/bin/rails
$ type -a rails
rails is /usr/local/bin/rails
rails is /usr/bin/rails
どうも、 /usr/local/bin
と/usr/bin
の2箇所にrailsコマンドがあるようです。PATH変数でより左にある/usr/local/bin
の方が優先なので、実際には/usr/local/bin/rails
が呼ばれているはずです。
ですが先述のように、railsと/usr/local/bin/rails
は異なる結果を返すのです。不思議です。
ちなみに、もう一つのrails実行ファイル/usr/bin/rails
を実行すると次のようになります。
$ /usr/bin/rails
Rails is not currently installed on this system. To get the latest version, simply type:
$ sudo gem install rails
You can then rerun your "rails" command.
まさに問題の挙動です。
railsコマンドは /usr/bin/rails
なのでしょうか?
PATH変数の探索の優先度がおかしいのでしょうか?
こういうときは原典を読むか、うまいこと検索しましょう。
##原典を読む
ソースコードや公式ドキュメントを読むのが一番正しいです。
bashの「コマンド探索と実行」の章に怪しい記述がありました。
Bash uses a hash table to remember the full pathnames of executable files to avoid multiple PATH searches (see the description of hash in Bourne Shell Builtins). A full search of the directories in $PATH is performed only if the command is not found in the hash table.
http://www.gnu.org/software/bash/manual/bash.html#Command-Search-and-Execution
「bashはPATHの探索を実施する前にハッシュテーブルを参照する」ようなことが書いてあります。
hashについてもこのドキュメントに記述があります。コマンドをkey, full pathnameをvalueとするkey-value型のデータ構造(ハッシュ, 辞書, 連想配列)を持ったbuiltinコマンドのようです。
どうやらhashコマンドが怪しそうです。
##検索する
検索する場合、問題に関係ありそうな単語を並べます。
今回は、railsコマンドを実行したときに呼ばれる実行ファイルが想定したものと違う・どうもbashのコマンドのPath探索の優先順位がおかしいということなので、以下の単語で検索してみました。
bash path order wrong
2番目のタイトル(bashが削除した実行ファイルを覚えてるんだけど。削除したはずなのに…)なんかが怪しそうです。
質問: which pip3で得た
/usr/local/bin/pip3
の結果とpip3
の結果が違う。
回答:hash -t pip3
の結果はどうなる? PATHの前にhashが使われるぞ
今回の問題と同じようなことを言っていますね。
hashというのが怪しそうです。
##hashが原因かどうか確認する
先ほど調べた通り、bashでコマンドを打つとPATHの探索の前にハッシュを見るようです。
hash -t コマンド名
でコマンド名に対するハッシュが記録されているかどうか、記録されている場合はその実行ファイルのフルPathを確認することができます。
まずは記録されていないパターンでどのような表示になるかを確認します。
$ hash -t hoge
-bash: hash: hoge: not found
問題のrails
コマンドを確認します。
$ hash -t rails
/usr/bin/rails
😦😦😦😦😦
ハッシュにrailsコマンドが記録されていました!
しかもハッシュに記録されている実行ファイルが/usr/bin/rails
なので、railsコマンドを実行した時になぜか/usr/local/bin/rails
が呼ばれないという問題に説明がつきます。
ということで、hashが犯人でした。
コマンドが実行される際、PATHを探索する前にhashが捜査され、hashがフルPathを記録していればその実行ファイルが実行されます。railsをインストールするPathとhashのPathが異なるため、いつまでたってもrailsをインストールできない(ように見えた)わけです。
#対処方法
何回/usr/local/bin/rails
にRuby on Railsをインストールしてもrails
コマンドの結果が変わらなかった問題に対して、hashにrailsの実行ファイルのフルPathは/usr/bin/rails
と記録されていることが原因だとわかりました。
解決策は、hashの値を使わずに改めてPATH変数の探索をさせることです。
方法はいくつかあります。
##hashコマンドでPATHを再探索させる
hash rails
でrailsコマンドの実行ファイルを再探索してhashに記録します。
PATH変数の優先順位から、/usr/local/bin/rails
が読み込まれるようになります。
$ hash -t rails # 事前確認
/usr/bin/rails
$ hash rails # 再読み込み
$ hash -t rails # 事後確認
/usr/local/bin/rails
##hashテーブルを全消去する
いっそhashテーブルを全消去してしまっても構いません。
次回railsコマンドが実行される時にhashに記録されていないのでPATHが探索されます。
$ hash -t rails # 事前確認
/usr/bin/rails
$ hash -r
$ hash -t rails # 事後確認
/usr/local/bin/rails
##PATHの変更
他にも、PATH変数の内容が変更された場合にもhashの内容は全部クリアされるようです。
#おまけ
##どうして今回問題になったのか
普通は、インストールするコマンドは事前に実行しようとしても実行ファイルがPATH上に存在せずnot foundになります。そのため、インストール前にhashに記録されるなんてことはあまりありません。
今回は、最初に無邪気にrails --version
を実行した時に /usr/bin/rails
がhashに乗ってしまい、同じコマンドrails
が別Path /usr/local/bin/rails
にインストールされたために問題が起きています。
おそらく同じ問題でハマった先人たち
Rails is not currently installed on this system.
で検索すると多くの人が同じ問題にハマっています。
rbenvのPATHを設定して直している人が多いのですが、~/.rbenv/shims/
の追加ってrbenvのインストールの時にやっているはずなので、PATHを設定したことによってhashが消去されたため結果的に直っただけなんじゃないか、と思っています。rbenv環境下でやっていないので要検証。
- https://qiita.com/w7tree/items/0860e2856f2429b20eee
- http://tomoprog.hatenablog.com/entry/2017/02/03/015936
typeコマンド
whichコマンドと似たことができるコマンドで typeコマンドというのがあります。
どうもwhichはhashまで考慮してくれないようなのですが、こちらのコマンドであればhashのキャッシュが効いていることがわかります。
$ type rails
rails is hashed (/usr/bin/rails)
まとめ
-
Rails is not currently installed on this system.
が無限に続くときは実行ファイルを確認して、hashが実行ファイルの場所をキャッシュしているようであれば再読み込みしましょう。 - bashはhashでコマンドのフルPathをキャッシュしているので$PATHを盲信しないようにしましょう。
参考