zsh だと PATH 設定を $ZDOTDIR/.zshrc で定義していたんですが、調子にのって .zshenv に定義するようにしたら PATH の挙動がおかしくなりました。 期待してたのは以下のような順番だったのですが、実際には /usr/local/bin、/usr/bin、/bin、… が先に来る。
$ echo $PATH | tr ':' '\n' /usr/local/opt/coreutils/libexec/gnubin /usr/local/opt/findutils/bin /Users/kiririmode/.rbenv/shims /Users/kiririmode/.plenv/shims /Users/kiririmode/.pyenv/shims /Users/kiririmode/.plenv/bin /Users/kiririmode/.rbenv/bin /usr/local/bin /usr/bin /bin /usr/sbin /sbin /opt/X11/bin
原因がわからなかったので、/etc/zprofile を見ると以下のようなスクリプトになっていることを発見。
$ cat /etc/zprofile # system-wide environment settings for zsh(1) if [ -x /usr/libexec/path_helper ]; then eval `/usr/libexec/path_helper -s` fi
path_helper の動作
この path_helper、man を読めば分かりますが、/etc/paths、/etc/paths.d からパスを読み込んで、PATH 環境変数に設定するユーティリティになっています。 実際に動かしてみると一目瞭然で、
$ echo $PATH A:B $ /usr/libexec/path_helper -s PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:A:B"; export PATH;
というように、元々設定していた PATH 環境変数よりも高い優先度で /etc/paths、/etc/paths.d に定義されたパスを設定するという挙動をする。 この path_helper が zprofile で実行されるということは、その前段で実行される /etc/zshenv、$ZDOTDIR/.zshenv でパスを定義していても、意図したパスの順序にならないということになります。
結論
変な落とし穴にハマらないためには、PATH は素直に $ZDOTDIR/.zshrc に定義しておいた方が良さそうですね。