Audit and clean shell PATH pollution in .bashrc, .zshrc, .zshenv, .profile. Use when PATH has junk, wrong binary resolves, temp dirs in PATH, or duplicates.
Audit and clean shell PATH pollution in .bashrc, .zshrc, .zshenv, .profile. Use when PATH has junk, wrong binary resolves, temp dirs in PATH, or duplicates.
PATH Rationalization
Quick Router
Symptom
Action
which foo returns /tmp/... or /run/...
Junk PATH entry — audit + remove
Binary just installed but old version runs
Shadowed by stale copy — find all with type -a foo
PATH has 40+ entries
Accumulated cruft — full audit needed
New shell starts slow
Excess PATH entries — rationalize
Safety Protocol (NON-NEGOTIABLE)
1. BACKUP every file before editing
2. VERIFY the backup exists and matches
3. EDIT the file
4. TEST in subshell (zsh -l -c 'echo $PATH | tr : \\n')
5. Only THEN tell user to source or open new shell
6. If anything breaks: restore from backup immediately
Never leave the user's shell broken. A bad .zshrc edit can lock someone out of their machine.
Workflow
Step 1: Inventory — Dump current PATH, find all shell config files
Step 2: Backup — Copy each file to ~/.path-rationalization-backup/
Step 3: Classify — Separate legitimate vs junk PATH entries
— Remove junk, deduplicate, fix ordering
Step 4: Edit
Step 5: Verify binary resolution — type -a <binary> for key tools
Step 6: Test — Subshell test before sourcing
Step 7: Install binaries — Copy to canonical location
Step 8: Confirm — User sources config, runs which <binary>
Step 1: Inventory
# Full numbered PATH dumpecho$PATH | tr':''\n' | cat -n
# All shell config files that modify PATH
grep -rn "PATH" ~/.zshrc ~/.zshenv ~/.zprofile ~/.profile ~/.bashrc ~/.bash_profile 2>/dev/null | grep -v "^#"# Count PATH modifications per file
grep -c "PATH" ~/.zshrc ~/.zshenv ~/.zprofile ~/.profile ~/.bashrc ~/.bash_profile 2>/dev/null
# Find all copies of a specific binarytype -a ntm # or whatever binary
Step 2: Backup
mkdir -p ~/.path-rationalization-backup
for f in ~/.zshrc ~/.zshenv ~/.zprofile ~/.profile ~/.bashrc ~/.bash_profile; do
[ -f "$f" ] && cp"$f" ~/.path-rationalization-backup/
donels -la ~/.path-rationalization-backup/
Verify backups exist before proceeding. Read back a backup file to confirm content matches.
Step 3: Classify PATH Entries
Junk (remove immediately)
Pattern
Source
Why Junk
/tmp/.tmp*, /data/tmp/.tmp*
Installer tests
Ephemeral temp dirs
/tmp/*-test*, /tmp/*-install*
Test scaffolding
Never cleaned up
/run/user/*/fnm_multishells/*/bin
fnm shell isolation
Session-ephemeral, not persistent
Duplicate entries
Appended on every shell start
Accumulate over time
Legitimate (keep, but deduplicate)
Path
Purpose
~/.local/bin
User binaries (canonical install location)
~/.cargo/bin
Rust toolchain
~/go/bin
Go binaries
~/.bun/bin
Bun runtime
~/.nvm/versions/node/*/bin
Node.js via nvm
~/.atuin/bin
Atuin shell history
/usr/local/bin, /usr/bin, /bin
System paths
/snap/bin
Snap packages
$FNM_MULTISHELL_PATH/bin
fnm Node.js (dynamic, OK in zshrc)
Requires Judgment (ask user)
~/mcp_agent_mail — Is this still needed in PATH?
Custom project dirs — Check if binary is actually there
Vault/cloud SDK paths — May be needed for specific workflows
When in doubt, ASK the user. Don't remove paths you aren't sure about.
Step 4: Edit
File Roles (Know Which File Does What)
File
Shell
When Loaded
Use For
~/.zshenv
zsh
Every zsh invocation (interactive + non-interactive)
PATH for tools that scripts need
~/.zshrc
zsh
Interactive shells only
Aliases, completions, prompts, interactive PATH
~/.zprofile
zsh
Login shells only
Login-time setup (rare)
~/.profile
bash/sh
Login shells
PATH for bash login sessions
~/.bashrc
bash
Interactive non-login
Bash aliases, functions, interactive PATH
~/.bash_profile
bash
Login shells
Bash login setup
Ordering Principle
PATH entries are searched left-to-right. Order should be:
# GOOD: won't duplicate on re-sourcecase":$PATH:"in
*:"$HOME/.local/bin":*) ;;
*) export PATH="$HOME/.local/bin:$PATH" ;;
esac# BAD: duplicates every time shell config is sourcedexport PATH="$HOME/.local/bin:$PATH"
Anti-Patterns to Remove
# REMOVE: bare prepend without guard (duplicates on every source)export PATH="/some/path:$PATH"# REMOVE: temp directory pathsexport PATH="/tmp/.tmpXYZ123:$PATH"export PATH="/data/tmp/.tmpABC456:$PATH"# REMOVE: test installer leftoversexport PATH="/tmp/jsm-test-install:$PATH"export PATH="/tmp/am-install-test:$PATH"
Step 5: Verify Binary Resolution
After editing, verify key binaries resolve correctly:
# In a subshell (doesn't affect current session)
zsh -l -c 'type -a ntm; type -a node; type -a cargo; type -a bun'# Check the new PATH is clean
zsh -l -c 'echo $PATH | tr : \\n | cat -n'# Verify no duplicates
zsh -l -c 'echo $PATH | tr : \\n | sort | uniq -d'# Verify binary actually exists and works at resolved path
zsh -l -c 'which ntm && ntm version'