Mac や Linux ではよく使われる direnv ですが、Windows の PowerShell ではインストールするだけでは動かず、使いたいけど使えないという人も多いかと思います。
このドキュメントでは Windows 11 / PowerShell 7 で direnv を安定して動かすことができたので設定方法をまとめておきたいと思います。
1. 前提条件
- Windows 10 / 11
- PowerShell 7.2 以上
- Git for Windowsがインストール済みであること(付属の bash を direnv が内部で使うため)
2. direnv のインストール
PowerShell 7 で次のコマンドを実行します。
winget install direnv.direnv
3. PowerShell プロファイル($PROFILE)の設定
① プロファイルを開く
code $PROFILE
私は VSCode 環境の code を使いますが、notepad $PROFILE でも開けます。ファイルが見つからない場合は New-Item -Type File -Path $PROFILE -Force を実行してから開いてください。
② プロファイルの末尾に追記する内容
次の内容をプロファイル(Microsoft.PowerShell_profile.ps1)に追記します。
direnv 公式のフックを使うのではなく、自前のフックに置き換えるので長いです。
# --------------------------------------------------
# direnv 設定
# --------------------------------------------------
# 1. Git for Windows 付属の bash.exe を明示(WSL の bash は使わない)
$env:DIRENV_BASH = "C:\Program Files\Git\bin\bash.exe"
# 2. 設定・キャッシュの保存先(ホーム直下にまとめる)
$env:DIRENV_CONFIG = "$HOME\.direnv\config"
$env:XDG_CACHE_HOME = "$HOME\.direnv\cache"
$env:XDG_DATA_HOME = "$HOME\.direnv\data"
# 3. direnv フック(公式の `direnv.exe hook pwsh` の代替)
$ExecutionContext.InvokeCommand.LocationChangedAction = {
$raw = (direnv.exe export pwsh) -join [Environment]::NewLine
if ([string]::IsNullOrWhiteSpace($raw)) { return }
$ast = [System.Management.Automation.Language.Parser]::ParseInput($raw, [ref]$null, [ref]$null)
$sets = @(); $unsets = @(); $setKeys = @{}
foreach ($st in $ast.EndBlock.Statements) {
$t = $st.Extent.Text
if ($t -match '^\$\{env:([^}]+)\}=') {
$setKeys[$Matches[1].ToUpperInvariant()] = $true
$sets += $t
} elseif ($t -match "^Remove-Item -LiteralPath 'env:/([^']+)'") {
$unsets += @{ Key = $Matches[1]; Text = $t }
} else {
$sets += $t
}
}
foreach ($u in $unsets) {
if (-not $setKeys.ContainsKey($u.Key.ToUpperInvariant())) { Invoke-Expression $u.Text }
}
foreach ($s in $sets) { Invoke-Expression $s }
}
③ プロファイルを再読み込み
. $PROFILE
フックを置き換える理由
今回は公式のフックは使わず、代替版を自作しています。
direnv による環境変数設定の流れについてですが、まず PowerShell 側の環境変数を読み込み、Git for Windows の bash(MSYS 版)で .envrc を評価し、実行前と実行後の差分 を PowerShell 用のコマンドに変換して返します。PowerShell はそのコマンドを実行して環境変数を書き換えます。
問題はこの bash です。MSYS 環境は起動時に、Windows の環境変数名を Path → PATH のように 大文字に変換 します。一方、direnv は変数名の大文字小文字を区別して差分を取るため、.envrc の中身に関係なく「Path が削除され、PATH が追加された」と認識してしまいます。
その差分が PowerShell に戻ると、今度は逆の事情が働きます。Windows では Path と PATH は同じ変数なので、「PATH の削除」と「PATH の設定」が同じ変数に対して実行されることになります。しかも この 2 つの実行順はランダムのため、削除が後になった場合は PATH が丸ごと消えてエラーになります。順番が運よく「削除→設定」になれば正常に動くので、「たまに壊れる」という厄介な状態になります。
この問題は direnv の GitHub でも Issue #1274 として報告されていますが、本記事執筆時点(v2.37)では未修正です。そこで上のフックでは、direnv の出力をそのまま実行せず、「同じ名前(大文字小文字無視)で追加もされる変数の削除は実行しない」という処理を挟んでいます。
4. Python 仮想環境(venv)の作成
ここからは、プロジェクトごとに Python の仮想環境を自動で切り替える例で進めます。まずプロジェクトのディレクトリに移動し、仮想環境を作成します。
cd path\to\your\project
uv venv
uv を使っていない場合は、標準の venv でも同じです。
python -m venv .venv
どちらの方法でも、プロジェクト直下に .venv フォルダが作られます。
5. プロジェクトでの .envrc の書き方
プロジェクト直下に .envrc を作成します。
code .envrc
.envrc は bash で読まれるので、Windows の絶対パス(C:\...)を書くと失敗しがちです。相対パスを渡すのが安心です。
# venv の Scripts フォルダを PATH の先頭に追加
PATH_add ./.venv/Scripts
# 仮想環境ルート
export VIRTUAL_ENV="./.venv"
6. 動作確認
① .envrc の承認(初回および変更時)
.envrc を作成・変更した直後は、direnv がセキュリティのため読み込みをブロックします。プロジェクト内で承認します。
direnv allow
② ディレクトリの出入りで確認
cd ..
cd .\project\
where.exe python
何度出入りしてもプロンプトが崩れず、出力の 1 行目に ...\.venv\Scripts\python.exe が出ていれば完了です。
まとめ
Windows での direnv は少し癖がありますが、押さえるべき点は 4 つです。
- PowerShell 7 を使う(5.1 は非対応)
$env:DIRENV_BASHで Git for Windows の bash を明示する- 公式フックを自前のフックに置き換える(環境変数名の大文字小文字問題の回避)
.envrcではPATH_addに相対パスを渡す
「たまに壊れる」系のトラブルは原因が見えにくいですが、direnv の場合はプロジェクト内で direnv export pwsh を実行して生の出力を眺めると、すぐに正体がわかります。同じ構成で悩んでいる方の参考になれば幸いです。
