PATHと環境変数の超入門(Mac / Windows / WSL)— “コマンドが見つからない”を根絶する

に公開

ねらいcommand not found や “別のPythonが動く” を根本から防ぎ、どのPCでも同じ挙動を出す。
対象:AI/WEB開発の初〜中級者/チーム開発者。
ゴール:PATHと環境変数を安全に追加・確認・共有できる。


TL;DR(まずはこれだけ)

  • PATHは“左から順に”検索される(左が勝つ)。追加は先頭にprependが基本。
  • 設定ファイルは場所を間違えない(Macは .zprofile.zshrc、WindowsはUser PATHが安全)。
  • 現セッションで動いても永続しないことがある(Windowsのsetx新しいシェルから有効)。
  • 機密は .env に分離、リポジトリには**.env.exampleのみ**を置く。

1) まず“今”の状態を確認する

共通(どのOSでも)

# どのPython/どのpipが使われているか
python -c "import sys;print(sys.executable)"
python -m pip --version

# PATHを1行ずつ見る(macOS/Linux/WSL)
echo "$PATH" | tr ':' '\n' | nl

Windows(PowerShell)

# どのpythonか
(Get-Command python).Source
# PATHを1行ずつ
$env:PATH -split ';' | % {$_} | % {$i=0}{++$i; "$i`t$_"}

原則:失敗の8割は“想定外の実行器”が先頭にあること。順序を可視化してから直す。


2) macOS(zsh)の正しい置き場所と書き方

  • どこに書く?

    • ~/.zprofile:ログイン時に1回だけ読ませたいもの(PATHの初期化)。
    • ~/.zshrc:毎回の対話シェルで読みたいもの(エイリアス、関数、補完など)。
    • HomebrewはIntel/M1でパスが違うので分岐する。

推奨スニペット(貼るだけ)

# ~/.zprofile  ← PATHの初期化はこっち
# Homebrew
if [[ -d /opt/homebrew/bin ]]; then
  eval "$(/opt/homebrew/bin/brew shellenv)"   # Apple Silicon
elif [[ -d /usr/local/bin ]]; then
  export PATH="/usr/local/bin:$PATH"          # Intel
fi

# よく使うローカルbinを先頭に(重複追加を防ぐ)
prepend_path () { case ":$PATH:" in *":$1:"*) ;; *) export PATH="$1:$PATH";; esac }
prepend_path "$HOME/.local/bin"
prepend_path "$HOME/.poetry/bin"
# ~/.zshrc  ← 対話設定はこちら
# 補完やエイリアスなど
autoload -U compinit && compinit
alias ll='ls -alF'

# PATHの見える化(任意)
path() { echo "$PATH" | tr ':' '\n' | nl; }
  • 反映:新しいターミナルを開くか、exec $SHELL -l(ログインとして再起動)。
  • アンチパターン.zshrc.zprofileの両方でPATHを上書き → 順序が壊れる

3) Windowsの基本(User PATHを安全に)

  • スコープの違い

    • Process(現在のシェルだけ)
    • User(自分のアカウント全体)←まずはこれ
    • System(全ユーザ)←管理者用途。失敗時の影響が大きいので避ける

一時的に試す(現セッションのみ)

# 先頭に追加(再起動不要・セッション限定)
$env:Path = "C:\Users\<you>\AppData\Local\Programs\Python\Python39\Scripts;" + $env:Path

永続化(ユーザPATHへ・安全版)

# 既存のUser PATHを取得
$u = [Environment]::GetEnvironmentVariable('Path','User')
# 追記(重複チェックは必要に応じて)
$new = "C:\Tools\bin;$u"
[Environment]::SetEnvironmentVariable('Path',$new,'User')
  • 反映新しいPowerShellを開く(既存セッションには効かない)。
  • 注意setx は簡単だが長いPATHを切り詰めることがある。上の .NET API 方式が安全。
  • スペースを含むパス"C:\Program Files\Git\cmd" のように必ず二重引用符で扱う。

4) WSL2(Windows上のLinux)でのコツ

  • プロジェクトはLinux側に置く\\wsl$\Ubuntu\home\<you>\project/mnt/c/... はI/Oが遅い)。
  • Windows PATHの混入を制御(再現性重視):
    /etc/wsl.conf(ディストロ内)に追記 → wsl --shutdown → 再起動。
[interop]
appendWindowsPath=false

[automount]
options="metadata,umask=22,fmask=11"  # 権限の整合を取りやすくする
  • DNSや時計ズレでネット/証明書が失敗することがある → wsl --status / wsl --update で更新、Windows側の時刻同期。

5) 環境変数の扱い(.env / 12-Factor)

  • 原則

    • 秘密はコードに直書きしない.env に置く。
    • リポジトリには .env をコミットしない、代わりに .env.example を置く。
    • 本番はOSの環境変数シークレットマネージャで注入。

最小雛形

# .env (コミットしない)
DATABASE_URL=postgres://user:pass@localhost:5432/app
SECRET_KEY=change-me

# .env.example (コミットする)
DATABASE_URL=postgres://user:pass@localhost:5432/app
SECRET_KEY=__FILL_THIS__
  • Python(ローカル実行)python-dotenv で読み込む。
# app.py
from dotenv import load_dotenv; load_dotenv()
# os.environ['SECRET_KEY'] が使える
  • Docker Composecomposeファイルと同じディレクトリの .env が自動読込。実際に反映された値は docker compose config で確認。

6) “順序”と“競合”の落とし穴(典型パターン)

  • 別のPythonが先頭にいる

    • 例:Xcode付属のPython、旧Anaconda、古い/usr/local/bin/python など
    • 対処:PATH順序を入れ替えるか、実行は常に python -m
  • 同名コマンドの多重インストール

    • 例:pip が3つある → 何が先か which -a pip / Get-Command pip -All で洗い出す
    • 対処:python -m pip 原則で回避。
  • 改行コード(CRLF)でスクリプトが動かない(WSL/mac)

    • 対処:.gitattributeseol=lf、既存は dos2unix
  • 設定を上書き合っている

    • 例:.zprofile.zshrc、複数のsetx、複数の.env
    • 対処:“どこに何を書くか”を決めてドキュメント化。被らせない。

7) チームで共有する“安全テンプレ”

macOS(貼るだけ)

# ~/.zprofile
if [[ -d /opt/homebrew/bin ]]; then
  eval "$(/opt/homebrew/bin/brew shellenv)"
elif [[ -d /usr/local/bin ]]; then
  export PATH="/usr/local/bin:$PATH"
fi
prepend_path () { case ":$PATH:" in *":$1:"*) ;; *) export PATH="$1:$PATH";; esac }
prepend_path "$HOME/.local/bin"
prepend_path "$HOME/.poetry/bin"

Windows(PowerShellスクリプト)

# add-path.ps1
param([string]$NewPath)
$u = [Environment]::GetEnvironmentVariable('Path','User')
if ($u -notlike "*$NewPath*") {
  [Environment]::SetEnvironmentVariable('Path', "$NewPath;$u", 'User')
  "Added to User PATH: $NewPath`nRestart the terminal."
} else { "Already present: $NewPath" }

.gitattributes(改行の地雷排除)

*.sh text eol=lf
*.py text eol=lf
Dockerfile text eol=lf

8) 診断の順番(困ったらこれ)

  1. 実行器の特定sys.executable / Get-Command python
  2. PATHの並びを見るtr ':' '\n' / -split ';'
  3. 仮想環境の有効化source .venv/bin/activate / .\.venv\Scripts\activate
  4. python -m原則で再実行python -m pip, python -m pytest
  5. 設定ファイルを最小化.zprofile だけで再現 → 問題切り分け
  6. WSLなら置き場所を確認:Linux側か?metadata有効か?
  7. 新しいシェルで確認:Windowsは特に再起動必須

まとめ(今日からの運用)

  • PATHは先頭勝ち。追加はprepend、重複防止の関数で。
  • 設定の責務分離:macOSは.zprofile(PATH)と.zshrc(対話)、WindowsはUser PATH中心。
  • 機密は .env、リポジトリは**.env.example**で共有。
  • 迷ったら**実行器の可視化→PATHの順序→python -m**で切り分け。

Discussion