[Zsh] Supabaseの接続先(local/remote)をターミナルに表示して切り替える
問題:今どちらのDBにつながっているかわからない
Supabaseを使った開発では、ローカルのSupabase(http://127.0.0.1:54321)とリモートのSupabase(https://xxx.supabase.co)を切り替えながら作業することがある。
切り替え自体は .env.local を書き換えるだけなので簡単だが、作業中に「今どちらのDBに接続しているか」が分からなくなることがある。
# .env.local(ローカル接続時)
# VITE_SUPABASE_URL=https://xxx.supabase.co ← コメントアウト
VITE_SUPABASE_URL=http://127.0.0.1:54321 ← こちらが有効ファイルを開いて確認すれば分かるが、毎回確認するのは手間。
解決策:RPROMPT(右プロンプト)に常時表示する

~/.zshrc にprecmdフックを追加し、Supabaseの接続先を右プロンプト(RPROMPT)に表示する。
# ~/.zshrc に追加
function _supabase_env_precmd() {
local envfile dir="$PWD"
while [[ "$dir" != "/" ]]; do
[[ -f "$dir/frontend/.env.local" ]] && { envfile="$dir/frontend/.env.local"; break; }
dir="${dir:h}"
done
if [[ -n "$envfile" ]]; then
local url
url=$(grep -E '^VITE_SUPABASE_URL=' "$envfile" | tail -1 | cut -d= -f2-)
if [[ "$url" == *"127.0.0.1"* ]]; then
_SB_ENV="%F{cyan}[DB:local]%f"
else
_SB_ENV="%F{red}[DB:remote]%f"
fi
else
_SB_ENV=""
fi
}
add-zsh-hook precmd _supabase_env_precmd
RPROMPT='$_SB_ENV'※ tail -1:同じキーが複数書かれている場合を考慮して、最後の行を採用している。
source ~/.zshrc で反映すると、プロジェクトディレクトリ内では右端に表示される。
- ローカル接続時:シアンで
[DB:local] - リモート接続時:赤で
[DB:remote] - プロジェクト外のディレクトリ:何も表示されない
仕組みの説明
precmdフックとは
precmd は、プロンプトが表示される直前に毎回呼ばれるzshのフック。add-zsh-hook precmd で関数を登録すると、コマンドを実行するたびに自動的に処理が走る。
ディレクトリを遡る理由
プロジェクトのサブディレクトリで作業していることも多いため、カレントディレクトリから親に向かって frontend/.env.local を探している。ルートディレクトリまで遡っても見つからなければ何も表示しない。
RPROMPTへの表示
zshには左プロンプト(PROMPT)と右プロンプト(RPROMPT)がある。既存のテーマ(PreztoのSorinテーマなど)が PROMPT を管理しているため、RPROMPT に出すことでテーマの設定を壊さずに済む。
セキュリティ上の問題はないか
.env.local にはAPIキーなどの秘密情報が含まれることがあるため、気になるところ。
しかし、今回の実装は source や eval を使わず、grep でテキストとして読み取るだけ。
url=$(grep -E '^VITE_SUPABASE_URL=' "$envfile" | tail -1 | cut -d= -f2-)
if [[ "$url" == *"127.0.0.1"* ]]; then取得した値は [[ ... ]] の文字列比較にしか使わない。シェルとして実行されることはないため、.env.local の内容が任意コードであっても実行されることはない。
| 方法 | リスク |
|---|---|
source .env.local |
危険 — ファイルをシェルスクリプトとして実行 |
eval $(cat .env.local) |
危険 — 任意コードが実行される |
今回の grep + 文字列比較 |
安全 — 読み取りのみ、実行なし |
また、VITE_SUPABASE_URL の値は画面に表示されない。「ローカルかリモートか」という判定結果のみが表示されるため、APIキーなどが漏れる心配もない。
切り替え自体もコマンドで行う
接続先の表示だけでなく、切り替え自体もコマンドで行えると便利だと考え、それも実装した。
プリセットファイルを用意する
.env.local を直接書き換えるのではなく、ローカル用とリモート用のプリセットファイルを用意して丸ごとコピーする方式が安全でシンプル。
frontend/
.env.local # 現在有効な設定(自動生成)
.env.local.local # ローカル用プリセット
.env.local.remote # リモート用プリセット各プリセットファイルにはコメントアウトなしで完結した設定を書く。
# .env.local.local
VITE_SUPABASE_URL=http://127.0.0.1:54321
VITE_SUPABASE_PUBLISHABLE_KEY=sb_publishable_xxxx(ローカル用)# .env.local.remote
VITE_SUPABASE_URL=https://xxx.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY=sb_publishable_xxxx(リモート用).gitignore の注意点
一般的な .gitignore では *.local が設定されていることが多いが、このパターンは 末尾が .local のファイルのみにマッチする。
そのため.env.local.remoteのようなファイルは無視されない。
# .gitignore
.env.local
frontend/.env.local.* ← これを追加sb-switch 関数を追加する
現在の接続先を判定して、local ↔ remote をトグルする関数。
# ~/.zshrc に追加
function sb-switch() {
local dir="$PWD" root=""
while [[ "$dir" != "/" ]]; do
[[ -f "$dir/frontend/.env.local" ]] && { root="$dir"; break; }
dir="${dir:h}"
done
[[ -z "$root" ]] && { echo "frontend/.env.local not found"; return 1; }
local envfile="$root/frontend/.env.local"
local url
url=$(grep -E '^VITE_SUPABASE_URL=' "$envfile" | tail -1 | cut -d= -f2-)
if [[ "$url" == *"127.0.0.1"* ]]; then
cp "$root/frontend/.env.local.remote" "$envfile"
echo "Switched to remote"
else
cp "$root/frontend/.env.local.local" "$envfile"
echo "Switched to local"
fi
}source ~/.zshrc 後、プロジェクト内で sb-switch を実行するだけで切り替わる。RPROMPTの表示も次のプロンプト表示時に自動更新される。
ファイル書き換えのセキュリティ
.env.local を書き換える操作そのものに問題はない。ただし、コマンドで切り替えが楽になると「今どちらに接続しているか忘れる」リスクが上がる。RPROMPTの表示と組み合わせて使うことで初めて安全に運用できる。
コピー方式のメリットは、sed のような複雑な正規表現トグルが不要なため、バグでファイルを壊す心配がない点。
AIツール使用時のセキュリティ懸念
Claude CodeなどのAIコーディングツールを使う場合、.env ファイルのシークレットキーが意図せず読み取られるリスクがある。これはプロンプト表示・切り替えとは独立した問題として意識しておく必要がある。
Claude Codeには読み取りから除外するファイルを設定する仕組みがあり、.env.local を対象に含めることができる。ただし、ツールによっては .gitignore を無視してファイルを読み取る場合もあるため、
設定しているからといって完全に安全とは限らない。
この点については改めて調査してまとめる予定。
まとめ
.env.localをgrepで読み取りprecmdフックでRPROMPTに表示するだけで実現できる- プロジェクト外では表示されない、既存のプロンプトテーマを壊さないなど実用的
sourceやevalを使わない限り、envファイルの読み取りはセキュリティ上問題ない- 切り替えはプリセットファイルのコピー方式が最もシンプルで安全
*.localは末尾が.remoteのファイルにマッチしないため.gitignoreの明示追加が必要- AIツール使用時の
.envファイルの扱いは別途注意が必要