.bashrc をやめた
シェル環境を3層に分解した話

はじめに
ある朝、j コマンドが消えた。
zoxide のエイリアスとして使っていたはずなのに、command not found と返ってくる。Ghostty では動くが、VSCode の統合ターミナルでは動かない。Zed では挙動がまた違う。
「なぜ動くか説明できない環境」の完成だった。
これは、.bashrc に設定を積み上げ続けた結果の話だ。そして、それをやめることで得た「決定性(determinism)」の話でもある。
Before:.bashrc 全部盛り時代
Linux 開発を始めた当初、便利なツールを導入するたびに .bashrc に書き足していった。
# .bashrc(肥大化した状態)
eval "$(starship init bash)"
eval "$(zoxide init bash)"
eval "$(fzf --bash)"
eval "$(keychain --quiet --eval --agents ssh id_ed25519_work id_ed25519_personal)"
source ~/.bash_aliases
export PATH="\(HOME/.local/bin:\)PATH"
# ... secrets, completions, etc.
alias、completion、環境変数、ツールの初期化がひとつのファイルに混在していた。
これが引き起こした問題は主に3つだった。
① PATH の不整合
ツールによって「パスが通っていたり通っていなかったりする」状態になった。ターミナルを新しく開くたびに、動く/動かないが変わることがある。
② ターミナル間の挙動差異
Ghostty では動くコマンドが VSCode の統合ターミナルでは動かない。j コマンドの消失はその典型だった。
③ 再現性の欠如
「なぜ動くか」を説明できなくなった。動いているのが設定のおかげなのか、過去に実行した何かのおかげなのか、もう分からない。
気づき:Shell ≠ Environment
問題を整理していて、あることに気づいた。
Shell ≠ Environment
Shell = UI(インターフェース)
Environment = 実行条件
.bashrc が「OS 標準のシェル設定」と「開発環境の初期化」を同時に担っていた。これが混乱の根本だった。
問題はツール(zoxide や starship)ではなく、初期化経路の混在にあった。特に「login shell」と「interactive shell」で読まれるファイルが異なるという事実が、ターミナルごとの挙動差異を生んでいた。
After:3層に分解する
この気づきをもとに、環境を3層に分解した。
bash = OS標準・保全層
zsh = 開発環境層
terminal = 表示層(すべて zsh -l に統一)
bash を保全層に戻す
.bashrc を初期化した。
cp /etc/skel/.bashrc ~/.bashrc
残したのは HISTSIZE の拡張だけ。開発用の設定はすべて撤去した。
zsh に開発環境を集約する
開発ツールの初期化は .zshrc に移した。
# ~/.zshrc
eval "$(starship init zsh)"
eval "$(zoxide init zsh)"
source ~/.zsh_aliases
export PATH="\(HOME/.local/bin:\)PATH"
alias も .zsh_aliases として分離した。
ターミナルをすべて zsh -l に統一する
各ターミナルの設定を変更し、起動シェルを統一した。
Ghostty
command = /usr/bin/zsh -l
VSCode
"terminal.integrated.profiles.linux": {
"zsh": {
"path": "/usr/bin/zsh",
"args": ["-l"]
}
},
"terminal.integrated.defaultProfile.linux": "zsh"
Zed(1.0.0 仕様)
"terminal": {
"shell": {
"with_arguments": {
"program": "/usr/bin/zsh",
"args": ["-l"]
}
}
}
WezTerm(~/.wezterm.lua)
local wezterm = require 'wezterm'
local config = {
default_prog = { '/usr/bin/zsh', '-l' },
font = wezterm.font('JetBrainsMono Nerd Font'),
font_size = 13.0,
color_scheme = "Dracula",
enable_tab_bar = false,
}
return config
WezTerm は default_prog に配列形式でシェルと引数を渡す。フォントもここで統一できる。
ハマりどころ集
移行の過程でいくつか詰まった点を記録しておく。
VSCode だけアイコンが豆腐になる
症状:
Ghostty → OK
Zed → OK
VSCode → アイコンが □(豆腐)
原因は、VSCode のターミナルフォントが別設定になっていること。
"terminal.integrated.fontFamily": "JetBrainsMono Nerd Font Mono"
を追加して解決した。
フォント名の「設定名」と「実体名」がズレている
設定名: JetBrainsMono Nerd Font Mono
実体: JetBrainsMono NFM
Ghostty は fontconfig が解決してくれるので問題ない。WezTerm や Zed では JetBrainsMono NFM と実体名で指定する必要がある場合がある。
j コマンドが消えた
.bash_aliases に alias j='z' と書いていたが、zsh 移行後は .zsh_aliases に移す必要があった。
# ~/.zsh_aliases
alias j='z'
alias ji='zi'
\(0 と \)SHELL の表示が違う
echo $0 # → zsh
echo $SHELL # → /bin/bash
$SHELL はログインシェルの情報であり、現在起動しているシェルとは異なる場合がある。
export SHELL=/usr/bin/zsh
を .zshrc に追加して統一した。
成果
Before: .bashrc に全部盛り → 環境が不確定
After: bash=保全 / zsh=開発 / terminal=表示層 → 決定性を回復
- どのターミナルから起動しても同じ環境
- PATH 問題が消滅
- デバッグ対象がアプリケーション自体に限定できるようになった
「なぜ動くか説明できる環境」になった、というのが最大の成果だと思っている。
おわりに
.bashrc をやめることは、ツールを捨てることではなかった。
「環境に対する責任の所在を明確にすること」 だった。
Shell は UI だ。Environment は実行条件だ。この2つを同じファイルに押し込めていたことが、すべての混乱の源だった。
次の記事では、この移行直後に Obsidian Git の SSH 認証が壊れ、ssh-agent と Flatpak サンドボックスを相手に格闘した話を書く。


