理系学生日記

おまえはいつまで学生気分なのか

Mac の zsh で PATH を zshenv に設定するとハマる

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 に定義しておいた方が良さそうですね。