C.W.K.
Stream
Lesson 06 of 10 · published

PATH: How the Shell Finds Executables

~15 min · path, executable, type, which, whence

Level 0Window Tourist
0 XP0/95 lessons0/14 achievements
0/100 XP to next level100 XP to go0% complete

The lookup that runs every command

When you type git status, the shell does not search every folder on your disk. It walks $PATH — a colon-separated list of directories — left to right and runs the first git it finds. echo $PATH shows the list. which git tells you which one was picked.

Directory order matters

Order is everything. /opt/homebrew/bin:/usr/bin:/bin means Homebrew binaries beat system binaries. If /usr/bin came first, you would still get the system python3 instead of the Homebrew one. The fix is to prepend Homebrew with export PATH="/opt/homebrew/bin:$PATH" in .zprofile.

which vs type vs command -v

Three tools, three answers:

  • which git — external binary lookup. Misses aliases and functions.
  • type git — shell-aware. Tells you if it's an alias, function, builtin, or external binary.
  • command -v git — POSIX, scriptable. Returns the path or exits non-zero. Use this in scripts.

zsh's whence -a git shows all definitions — useful when one shadows another.

The hash cache

Shells cache the path of every command they have ever run, so they don't re-walk $PATH. After installing a new brew command you might still see "command not found" until you run hash -r (bash) or rehash (zsh) — that flushes the cache and forces a fresh lookup.

Don't put . in PATH

Adding . (the current directory) to $PATH looks convenient but it's a classic security footgun: anyone who can drop a file in a directory you cd into can hijack any command name. Run local scripts with explicit ./script.sh.

Code

Inspect what runs and why·bash
echo $PATH | tr ':' '\n'
which python3
type python3
command -v python3
# zsh only — show every definition shadow
whence -a python3
Prepend Homebrew on Apple Silicon·bash
# Add to ~/.zprofile so it runs at login
eval "$(/opt/homebrew/bin/brew shellenv)"
# Then in .zshrc you can rely on brew binaries first
# Verify
which brew && brew --prefix

External links

Exercise

Run echo $PATH | tr ':' '\n'. Note the order. Find your python3 with which python3 and type python3 — compare the answers. Then brew install jq, run jq --version. If you get "command not found," fix it with hash -r (don't restart the terminal). That's the cache.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.