環境変数の export と PATH:.zshrc での書く順番に注意
.zshrc や .bashrc でツールのパスを設定するとき、書く順番によって export が正常に機能しないことがあります。
問題のある設定例
# NG: この時点で mise は PATH に入っていないので失敗する export MISE_ROOT=$(mise root) # command not found: mise # mise を PATH に追加するのはここ(遅い) export PATH="$HOME/.local/bin:$PATH"
$(mise root) を評価しようとした時点では mise はまだ PATH に存在しません。結果として MISE_ROOT は空のまま、またはエラーになります。
なぜこうなるのか
.zshrc はシェル起動時に上から順に実行されます。コマンド置換 $(...) はその行が評価されるタイミングで即座に実行されるため、まだ PATH に追加されていないコマンドは command not found になります。
シェル起動 ↓ 1行目: export HOGE=$(func) ← この時点の PATH でコマンドを探す ↓ → func が見つからず HOGE は空になる 2行目: export PATH="$HOME/bin:$PATH" ← ここで func が PATH に追加される ↓ (1行目はすでに実行済み。HOGE は空のまま変わらない)
正しい書き方:PATH を先に設定する
コマンド置換を使う export は、そのコマンドが PATH に入った後に書く。
# OK: PATH を先に設定してから使う export PATH="$HOME/.local/bin:$PATH" # mise が PATH に入っているのでコマンド置換が正常に動く export MISE_ROOT=$(mise root)
よくある実例
Homebrew(macOS)
# NG export HOMEBREW_PREFIX=$(brew --prefix) # brew が PATH にない eval "$(/opt/homebrew/bin/brew shellenv)" # ここで brew が PATH に入る(遅い) # OK eval "$(/opt/homebrew/bin/brew shellenv)" # 先に brew を PATH に追加 export HOMEBREW_PREFIX=$(brew --prefix) # これで brew が使える
Go
# NG export GOPATH=$(go env GOPATH) # go が見つからない export PATH="$PATH:/usr/local/go/bin" # ここで go が PATH に入る(遅い) # OK export PATH="$PATH:/usr/local/go/bin" # 先に go を PATH に追加 export GOPATH=$(go env GOPATH) # これで go env が使える
pyenv / rbenv
# NG eval "$(pyenv init -)" # pyenv が見つからない export PATH="$HOME/.pyenv/bin:$PATH" # ここで pyenv が PATH に入る(遅い) # OK export PATH="$HOME/.pyenv/bin:$PATH" # 先に pyenv を PATH に追加 eval "$(pyenv init -)" # これで pyenv init が使える
絶対パスを使えば順番に依存しない
コマンドを絶対パスで指定すれば PATH に関係なく動作します。ただし環境依存になるため、インストール先が固定されているツールに限って使うのが無難です。
# 絶対パスなら順番に関係なく動く export HOMEBREW_PREFIX=$(/opt/homebrew/bin/brew --prefix)
まとめ
| パターン | 結果 |
|---|---|
PATH に追加する前に $(cmd) を使う |
command not found / 変数が空になる |
PATH に追加した後に $(cmd) を使う |
正常に動作する |
絶対パスで呼ぶ $(/usr/bin/cmd) |
順番に関わらず動作する |
.zshrc のデバッグには echo $PATH を途中に挟んで、その時点で PATH に何が入っているか確認するのが確実です。
mise を使ったバージョン管理については「miseでRubyをインストールする方法:.ruby-versionの使い方」も参照してください。