とっても便利なrbenv, ndenv, plenv, pyenvなどのxxenvシリーズでPATHにハマった記録。
TL; DR
- xxenvの実装やバージョンによっては、PATHに
~/.xxenv/shims
が2重に追加されないようになっている。 - この時、
bash -l
でコマンドを実行すると1、引き継がれたPATHにshimsが含まれるので2度目の追加がされず、shimsの優先順位が/usr/bin
などより低くなり、ビルトインのインタプリタに解決されて涙目になる。 - ドキュメントでは
~/.xxenv/bin
にPATHを通すように指示されているが、ついでに~/.xxenv/shims
に通しておくと確実か。
事の発端
$ which perl
/Users/shin/.plenv/shims/perl
$ bash -lc "which perl"
/usr/bin/perl
何故???
PATHを確認
echo $PATH | perl -nle 'print join("\n", split(":", $_))'
こんな感じのワンライナーで適当に目grep。
# echo $PATH
...
/Users/shin/.plenv/shims
/Users/shin/.plenv/bin
/Users/shin/bin
/usr/local/bin
/usr/local/bin
/usr/bin
...
# bash -lc "echo \$PATH"
...
/Users/shin/.plenv/bin
/Users/shin/bin
/usr/local/bin
/usr/local/bin
/usr/bin
...
/Users/shin/.plenv/shims
/Users/shin/.plenv/bin
/Users/shin/bin
確かに何かがおかしい模様。
なにをやっているか見てみる
頼れる味方-x
オプション
$ bash -lxc "echo hoge"
+ '[' -x /usr/libexec/path_helper ']'
++ /usr/libexec/path_helper -s
+ eval 'PATH="/usr/local/bin:/usr/bin:...";' export 'PATH;'
++ PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:...
++ export PATH
+ '[' /bin/bash '!=' no ']'
+ '[' -r /etc/bashrc ']'
+ . /etc/bashrc
++ '[' -z '' ']'
++ return
# 以下.bash_profile
どうも.bash_profile
の前に、path_helperなるものでPATHがゴニョゴニョされている様子。
そういえば、bashの設定の読み込み順がどうだったかというと...
bash が対話的なログインシェルとして起動されるか、 --login オプション付きの非対話的シェルとして起動されると、 /etc/profile ファイルが存在すれば、 bash はまずここからコマンドを読み込んで実行します。 このファイルを読んだ後、 bash は ~/.bash_profile, ~/.bash_login, ~/.profile をこの 順番で探します。 bash は、この中で最初に見つかり、かつ読み込みが可能であるファイルから コマンドを読み込んで実行します。 --noprofile オプションを使ってシェルを起動すれば、 この動作を行わないようにできます。
https://linuxjm.osdn.jp/html/GNU_bash/man1/bash.1.html
ということで/etc/profile
を確認すると
# System-wide .profile for sh(1)
if [ -x /usr/libexec/path_helper ]; then
eval `/usr/libexec/path_helper -s`
fi
if [ "${BASH-no}" != "no" ]; then
[ -r /etc/bashrc ] && . /etc/bashrc
fi
を発見。怪しい、怪しすぎる。
man path_helper
The path_helper utility reads the contents of the files in the directories /etc/paths.d and /etc/manpaths.d and appends their contents to the PATH and MANPATH environment variables respectively.
https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man8/path_helper.8.html
/etc/paths.d
以下のファイルを読み込むらしいが、
$ ls /etc/paths.d/
40-XQuartz
$ cat /etc/paths.d/40-XQuartz
/opt/X11/bin
パット見では悪さをする雰囲気ではなさそう?
/usr/libexec/path_helper -s
とりあえずコレの挙動を確認。
$ /usr/libexec/path_helper -s
PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Users/shin/.go/bin:...:/Users/shin/.plenv/bin:/Users/shin/bin"; export PATH;
現在のPATHの前に/usr/local/bin
などが、、、あれ、そもそもPATHに値入ってるのか。exportしているのだからそりゃそうか。
$ bash --noprofile -lc "echo \$PATH"
/Users/shin/.go/bin:...:/opt/X11/bin
あらためてPATHを見てみる。
$ bash -lc "echo \$PATH" | perl -nle 'print join("\n", split(":", $_))'
/Users/shin/.go/bin
/Users/shin/Documents/lab/go/bin
/usr/local/opt/httpd/sbin
/Users/shin/.pyenv/bin
/Users/shin/.ndenv/bin
/Users/shin/.rbenv/shims
/Users/shin/.rbenv/bin
/Users/shin/.plenv/bin
/Users/shin/bin
/usr/local/bin
/usr/local/bin # ここからpath_helper
/usr/bin
/bin
/usr/sbin
/sbin
/opt/X11/bin
...
path_helperより前の部分は.bash_profile
とかで付与されたPATHのはず。
しかしどうもrbenv以外はshimsへのPATHが張られていない模様。
shimsへのPATHはeval $(xxenv init -)
で追加されるはずなのだが、もしかすると既に張られているときは張らないような処理になっていると仮説を立ててみると...
# https://github.com/tokuhirom/plenv/blob/2.1.1/libexec/plenv-init#L76-L78
if [[ ":${PATH}:" != *:"${PLENV_ROOT}/shims":* ]]; then
echo 'export PATH="'${PLENV_ROOT}'/shims:${PATH}"'
fi
見事に当たった様子。
ただ、この部分、v2.2.0になって変わっているようで、
# https://github.com/tokuhirom/plenv/blob/2.2.0/libexec/plenv-init#L84-L93
case "$shell" in
fish )
echo "setenv PATH '${PLENV_ROOT}/shims' \$PATH"
echo "setenv PLENV_SHELL $shell"
;;
* )
echo 'export PATH="'${PLENV_ROOT}'/shims:${PATH}"'
echo "export PLENV_SHELL=$shell"
;;
esac
チェック無くなっとるー。
ということで
plenvのバージョンを2.2.0に上げたところ、
$ bash -lc "which perl"
/Users/shin/.plenv/shims/perl
解決しましたとさ。 (path_helper疑ってゴメン)
全てのxxenvで、この辺りの微妙な挙動が揃っているとは限らないので、いっそbinと一緒にshimsへのパスも追加しておいたほうが無難かもしれない。
-
bash -lc "ruby ..."
のようにPATH設定を手抜くのはxxenvで良くあるテク。 ↩