Skip to content

環境変数と PATH

💡 学習ガイド:ターミナルで gitpython と入力するたび、システムはそのプログラムがどこにあるかを探さなければなりません。コードから大規模モデル API を呼び出すたび、プログラムはどのキーを使うかを知る必要があります。この 2 つの背後にあるのは同じ仕組み——環境変数です。


0. すべてのプログラムは設定を持っている

実行中のすべてのプログラムは「キー=値」の設定セットを保持しており、これを環境変数と呼びます。プログラムはいつでもこれらの設定を読み取り、現在の実行環境を把握できます。

下のリストの変数をクリックして、ターミナルでその値を「確認」してください:

Environment Variable BrowserClick any variable row to inspect its value and purpose in the terminal
VariableExample value
HOME/Users/alice
USERalice
SHELL/bin/zsh
PATH/usr/local/bin:/usr/bin:/bin
PWD/Users/alice/projects
LANGen_US.UTF-8
NODE_ENVdevelopment
OPENAI_API_KEYsk-••••••••••••••••
bash
← Click any variable on the left to inspect it
$
Core concept:Environment variables are key=value configuration owned by each process. A program inherits a copy from its parent process at startup. You can inspect them with echo $VARIABLE and set them with export KEY=value.

1. PATH:シェルは入力されたコマンドをどう見つけるか

PATH は特別な環境変数で、コロンで区切られたディレクトリパスのリストを格納しています。git と入力すると、シェルはこのディレクトリリストの順番に従って git という名前の実行可能ファイルを一つずつ探します——最初に見つけた時点で直ちに停止します。

bash
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

コマンドを選択して、シェルがディレクトリを順に検索する過程を観察してください:

PATH Search ProcessEnter a command name and see how the shell searches directories
Choose command:
Current PATH:
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
/usr/local/bin
Waiting
/usr/bin
Waiting
/bin
Waiting
/usr/sbin
Waiting
/sbin
Waiting
Core mechanism:After the shell receives a command name, it searches directories in PATH order. The first match wins and the search stops. That makes PATH order important: earlier directories have higher priority.

3 つの重要なルール

  • PATH の中で前にあるディレクトリほど優先度が高い
  • 最初に見つかった時点で停止し、それ以上検索しない
  • どのディレクトリにもない → command not found

2. ツールをインストールした後、なぜターミナルを再起動する必要があるのか?

nvm、Homebrew、conda などのツールをインストールすると、インストールスクリプトが自動的に ~/.zshrc に 1 行追加し、自身のディレクトリを PATH に加えます:

bash
# インストーラが自動的に書き込んだ内容(例)
export PATH="/usr/local/opt/python@3.12/bin:$PATH"

この行は新しいシェルが起動した時にのみ実行されます。すでに開いているターミナルウィンドウは影響を受けないため:

bash
# 再起動せずにすぐ反映させる
source ~/.zshrc

AI 開発ツールでよくある状況

bash
# Ollama / pipx をインストールしたが command not found になる
which ollama          # 実際のインストール先を確認

# pip でインストールした CLI ツールのパス(PATH に追加)
# macOS:~/Library/Python/3.x/bin
# Linux:~/.local/bin
export PATH="$PATH:$HOME/.local/bin"

# 推奨:pipx で CLI ツールをインストール、PATH を自動管理
pipx install aider-chat

3. 変数のスコープ:誰がこの変数を見られるのか?

環境変数はすべてのプログラムにブロードキャストされるわけではありません——各プロセスは自身のコピーを保持し、親プロセスから継承します。自分のコピーを変更しても親プロセスには影響しません。

下の図は 3 つのレベルを示しています。「ユーザーレベル」で新しい変数をエクスポートし、それが「プロセスレベル」に表示されるか確認してください:

Three Levels of Environment VariablesVariables flow one way from outer scopes to inner scopes; child processes inherit a copy from parents
🖥️
System level /etc/environment
Visible to all users and processes; configured by an administrator
PATH=/usr/local/bin:/usr/bin:/bin
LANG=zh_CN.UTF-8
TZ=Asia/Shanghai
▼ Child process inherits the parent environment
👤
User level ~/.zshrc
Affects only the current user and is loaded when the login shell starts
HOME=/Users/alice
SHELL=/bin/zsh
NVM_DIR=$HOME/.nvm
=
▼ Start a child process, such as node app.js
⚙️
Process level (currently running program)
Inherits variables from upper levels, disappears on exit, and does not modify the parent process
PATH=/usr/local/bin:/usr/bin:/bin
LANG=zh_CN.UTF-8
TZ=Asia/Shanghai
HOME=/Users/alice
SHELL=/bin/zsh
NVM_DIR=$HOME/.nvm
NODE_ENV=development
PORT=3000
One-way flow:Variables are inherited downward only. Changing a variable in a child process does not affect its parent. Variables set with export in a terminal also disappear when that terminal closes.

4. export:子プロセスが変数を読み取れるかどうかを決める

変数を設定する際、export を付けるかどうかで全く異なる結果になります:

export Decides Whether Child Processes Can See a VariableToggle the switch and observe whether the child process can read a variable set by the parent
Parent process (Shell)
$MY_VAR="hello"
$echo $MY_VAR
hello
$bash -c 'echo $MY_VAR'
Start child process
Variable not inherited
Child process (bash -c ...)
$echo $MY_VAR
(empty output)
#A child process cannot modify parent variables
Without export: The variable exists only in the current shell, so the child process reads an empty string.

変数をセッションをまたいで永続化するには、export を設定ファイルに書き込みます:

bash
# macOS (zsh)
echo 'export MY_VAR="value"' >> ~/.zshrc
source ~/.zshrc       # すぐに反映、ターミナルを再開する必要なし

# Linux (bash)
echo 'export MY_VAR="value"' >> ~/.bashrc
source ~/.bashrc

5. API キー:絶対にソースコードに書いてはいけない

OpenAI、Anthropic、DeepSeek などの API を呼び出す際、キーはあなたの「身分証明書 + クレジットカード」のようなものです。漏洩すると、他人があなたの利用枠を使って消費でき、費用はあなたの負担になります。

最もよくある間違いは、キーをソースコードに直接書き込むことです:

Hard-coded Keys vs Environment VariablesThe same feature, two implementations, completely different security outcomes
Dangerous: key written in code
# Python
import openai
 
client = openai.OpenAI(
api_key="sk-proj-abc123..."
)
💀After git push, the key is public on GitHub
💀Crawlers can find it quickly and generate costs
💀GitHub Secret Scanner may revoke the key automatically
💀Deleting the commit is not enough because Git history keeps it
Correct: read from environment variable
# Python
import openai, os
 
client = openai.OpenAI(
api_key=os.environ.get("OPENAI_API_KEY")
)
The code contains no secret and can be open-sourced safely
Development, testing, and production can use different keys
If a key leaks, regenerate it without changing code
Team members can use separate keys without affecting each other
Golden rule:A secret string in code means the secret is already leaked. GitHub Secret Scanner can detect prefixes such as sk- shortly after a push and notify providers to revoke them. Even if you delete the commit, Git history still contains it.

6. ローカル開発:.env ファイルでキーを管理

ローカル開発では、プロジェクトのルートディレクトリにある .env ファイルにキーを保存し、コードは dotenv ライブラリを通じて読み取ります。.env は必ず .gitignore に追加し、Git にコミットしてはいけません。

左側で設定、右側で読み取り——言語を切り替えて 2 つの書き方を確認してください:

.env File + Code ReadingConfiguration on the left, code on the right; the variable name is the only link
📄 .env Do not commit
# Local development config, do not commit to Git
OPENAI_API_KEY=sk-proj-abc123...
DATABASE_URL=postgresql://localhost/dev
PORT=3000
NODE_ENV=development
📋 .env.example Can commit
# Copy to .env and fill in real values
OPENAI_API_KEY=(leave empty)
DATABASE_URL=(leave empty)
PORT=(leave empty)
NODE_ENV=(leave empty)
💻 main.py
# pip install python-dotenv openai
from dotenv import load_dotenv
import os, openai
 
load_dotenv() # Read .env file
 
client = openai.OpenAI(
api_key=os.environ.get("OPENAI_API_KEY")
)
 
db = os.environ.get("DATABASE_URL")
port = int(os.environ.get("PORT", 8000))
Values actually read by the program
OPENAI_API_KEYsk-proj-abc123...
DATABASE_URLpostgresql://localhost/dev
PORT3000
Workflow:load_dotenv() / import 'dotenv/config' reads the .env file at startup, injects its key-value pairs into the process environment, and the code reads them with os.environ or process.env. The two sides are connected only by variable names.

7. 本番環境:実行プラットフォームにキーを注入させる

.env は開発段階の便利なツールです。サーバーやクラウドプラットフォームでは、実行環境がキーの注入を担当すべきであり、コード自体はキーがどこに保存されているかを全く意識しないようにします:

How Production Injects Secrets.env is a development convenience; servers should not rely on it
/etc/systemd/system/myapp.service
# Recommended: use a separate secret file with controlled permissions
[Service]
EnvironmentFile=/etc/myapp/secrets.env
ExecStart=/usr/bin/node /app/index.js
# Set file permissions so only the owner can read it
sudo chmod 600 /etc/myapp/secrets.env
sudo chown deploy:deploy /etc/myapp/secrets.env
# Reload configuration and restart the service
sudo systemctl daemon-reload
sudo systemctl restart myapp
After chmod 600, only the deploy user can read the secret file; other accounts cannot access it
Secrets are separated from code, so rotating a key does not require redeploying code
Avoid writing Environment="KEY=val" directly in the systemd file; it requires reloads and leaves plaintext in config
Principle:.env files are convenient for local development. In production, the runtime platform should inject environment variables, while application code stays unaware of where secrets live or how they arrived.

8. 実践的なトラブルシューティング

command not found

bash
# ステップ 1:PATH にあるか確認
which python3         # 出力があれば見つかっている

# ステップ 2:プログラムの実際の場所を見つける(macOS)
brew list python | grep bin

# ステップ 3:ディレクトリを PATH に追加
export PATH="/見つかったパス:$PATH"
source ~/.zshrc       # 設定ファイルに書き込んだ後は source を忘れずに

2 つのバージョンをインストールしたが、使いたい方が使われない

bash
which python
# /usr/bin/python ← 古いシステムバージョン、PATH の前方にある

# 新しいバージョンのディレクトリを PATH の先頭に配置
export PATH="/usr/local/bin:$PATH"

which python
# /usr/local/bin/python ← 新バージョン、これで優先される

変数を設定したのに、プログラムが読み取れない

原因解決策
export を忘れたexport を追加して再試行
~/.zshrc を変更したが反映されていないsource ~/.zshrc を実行
.env を使っているが dotenv がインストールされていないpip install python-dotenv / npm install dotenv
サーバーで SSH セッション中のみ有効systemd EnvironmentFile を使用

用語早見表

用語意味
PATHシェルが実行可能ファイルを検索するディレクトリのリスト。コロン区切り、順序が優先度を決定
export変数を継承可能としてマークし、子プロセス起動時に自動的にコピーを取得
source現在のシェルで設定ファイルを再実行し、変更を即座に反映
whichコマンドに対応する実行可能ファイルのパスを表示(PATH 検索の結果)
.envプロジェクトローカルの設定ファイル。開発用キーを保存。必ず .gitignore に追加
.env.example変数名は完全だが値は空のテンプレート。Git に安全にコミット可能
chmod 600ファイル権限:所有者のみ読み書き可能。キーファイルの保護に適している
Secret ScannerGitHub などのプラットフォームがキーの漏洩を自動スキャンし、発見時にベンダーに取り消しを通知