PATH shell

command not found. the three most infuriating words in linux.

You installed something. The installer said “Success!” You typed the command. bash said “command not found.”

You installed it again. Same result. You Googled it. Someone on Stack Overflow said “add it to your PATH.” You didn’t know what that meant. You copied their answer. It worked. You moved on with your life. Six months later, you installed something else. Same problem. You Googled it again. Different Stack Overflow answer. You copied that one too. Your .bashrc now has four export PATH= lines that you don’t fully understand, and you’re terrified to touch any of them because something might break.

Let’s fix that. PATH is not complicated. It’s just poorly explained.

Unless you’re running Windows then wtf none of this applies to you. But hey, come to the dark side, go install WSL2 and you can follow along. We’ll wait. Impatiently.

If you’re lazy like me (all sysadmins are!) then click here for the PATH cheat sheet.


What PATH actually is

echo $PATH

Run that. You’ll see something like:

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

That’s a list of directories, separated by colons. That’s it. That’s the whole thing.

When you type a command — grep, python, hugo, anything — bash doesn’t search your entire computer for it. It goes through this list of directories, one by one, left to right, and looks for an executable with that name. First match wins. If it gets to the end without finding it, you get “command not found.”

That’s all PATH is: a search order for executables.

See it more clearly

echo $PATH | tr ':' '\n'
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin

Now it’s readable. bash searches these directories in this order. If grep is in /usr/bin/, bash finds it at position 4 and runs it.


Why “command not found” happens

The command exists. It’s installed. It’s on your machine. But it’s in a directory that isn’t in your PATH.

Find where the command actually is

which python3
/usr/bin/python3

which tells you the full path to the command bash is using. If which finds nothing, the command isn’t in any PATH directory.

find / -name "hugo" -type f 2>/dev/null

The nuclear option. Searches the entire filesystem. If this finds it, now you know where it is — and you know which directory to add to PATH.

Common locations that aren’t in PATH by default

Directory What installs there
~/.local/bin pip install --user, many user-level tools
~/go/bin Go binaries (go install)
~/.cargo/bin Rust binaries (cargo install)
/usr/local/go/bin Go itself (from the official tarball)
~/.npm-global/bin npm global packages (when configured)
/snap/bin Snap packages (usually auto-added but sometimes not)
/opt/*/bin Software installed to /opt

If you installed something from one of these ecosystems and got “command not found,” the fix is almost always adding that directory to PATH.


How to add to PATH (correctly)

The right way

Add this to your ~/.bashrc:

export PATH="$HOME/.local/bin:$PATH"

What this does:

  1. Takes your existing PATH ($PATH)
  2. Prepends $HOME/.local/bin to the front
  3. Exports the result

The $HOME expands to your home directory (e.g., /home/owner). Never hardcode /home/owner — use $HOME so it works if your username ever changes.

Prepend vs. append

# Prepend (your directory searched FIRST)
export PATH="$HOME/.local/bin:$PATH"

# Append (your directory searched LAST)
export PATH="$PATH:$HOME/.local/bin"

Prepend when you want your local version of a tool to take priority over the system version. Append when you want the system version to win.

Almost always prepend. If you installed a newer version of Python in ~/.local/bin, you want bash to find that one, not the ancient system Python.

Adding multiple directories

export PATH="$HOME/.local/bin:$HOME/go/bin:$HOME/.cargo/bin:$PATH"

One line. Multiple directories. All prepended. Clean.


The wrong ways to add to PATH

People get this wrong constantly. Here’s what not to do.

Don’t overwrite PATH

# WRONG — destroys your entire PATH
export PATH="/home/owner/mytools"

# RIGHT — adds to existing PATH
export PATH="/home/owner/mytools:$PATH"

If you forget the :$PATH at the end, you just replaced your entire PATH with one directory. Now ls, grep, cat, and literally every command is “not found.” Don’t panic. Open a new terminal. The old PATH is restored from your .bashrc. Fix the line. Never do that again.

Don’t put PATH in the wrong file

# In ~/.bashrc — runs for every new terminal (interactive shells)
export PATH="$HOME/.local/bin:$PATH"

# In ~/.profile or ~/.bash_profile — runs only for login shells

Put it in .bashrc. If you put it only in .bash_profile, new terminal tabs won’t have it. If you followed the advice on the .bashrc page to source .bashrc from .bash_profile, this is already handled.

Don’t duplicate PATH entries

Every time you open a terminal, .bashrc runs. If you have:

export PATH="$HOME/.local/bin:$PATH"

And you open ten terminals, you might end up with $HOME/.local/bin in your PATH ten times. It works fine but it’s messy. To prevent it:

case ":$PATH:" in
    *":$HOME/.local/bin:"*) ;;
    *) export PATH="$HOME/.local/bin:$PATH" ;;
esac

That checks if the directory is already in PATH before adding it. Overkill for most people, but satisfying for people who like clean echo $PATH output.


Debugging PATH problems

“command not found” after installing something

# 1. Find where it installed
find / -name "the-command" -type f 2>/dev/null

# 2. Check if that directory is in PATH
echo $PATH | tr ':' '\n' | grep "/the/directory"

# 3. If not, add it to ~/.bashrc
echo 'export PATH="/the/directory:$PATH"' >> ~/.bashrc
source ~/.bashrc

“wrong version” of a command

which python3

If this shows /usr/bin/python3 but you installed Python 3.12 in /usr/local/bin, the system version is winning because /usr/bin comes before /usr/local/bin in your PATH. Prepend the directory with the version you want:

export PATH="/usr/local/bin:$PATH"

See all versions of a command

which -a python3

Shows every python3 in your PATH, in search order. The first one wins.

type -a python3

Even better — also shows aliases and functions, not just executables.

Check if PATH changes are working

source ~/.bashrc
echo $PATH | tr ':' '\n'
which the-command

Reload, inspect, verify. In that order.


The system PATH

Your PATH has two layers: the system default and your additions.

The system PATH is set in /etc/environment and /etc/profile (and sometimes /etc/profile.d/*.sh). These are system-wide — they affect all users.

cat /etc/environment

Usually shows something like:

PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

Don’t edit this unless you’re the sysadmin and you know what you’re doing. Your personal additions go in ~/.bashrc.


The flags that actually matter

PATH isn’t a command, so there are no flags. But here are the tools and techniques:

Tool / Technique What it does
echo $PATH Show your current PATH.
echo $PATH | tr ':' '\n' Show PATH as a readable list.
which command Show which executable bash will run.
which -a command Show ALL matching executables in PATH order.
type -a command Show all matches including aliases and functions.
export PATH="dir:$PATH" Add a directory to the front of PATH.
hash -r Clear bash’s command cache (after moving executables).

“But—”

No.

“Windows just adds things to PATH with an installer checkbox.” Yes, and then the PATH grows to 2,000 characters across 40 entries that include three versions of Java, two Pythons, a Node.js from 2019, and whatever the hell “Oracle\Java\javapath” is. At least on Linux you’re aware of what’s in there because you put it there.

“I just use the full path every time.” You type /usr/local/go/bin/go build every time instead of go build? That’s not a workflow. That’s a cry for help.

“I set PATH in /etc/environment so it applies everywhere.” Congratulations, you just changed PATH for every user on the system. If this is your laptop, fine. If this is a server, your coworkers would like a word.

“My PATH is fine, I’ve never had this problem.” You’ve never had this problem because someone else configured your machine, or because you only use commands that came pre-installed. The first time you pip install or cargo install or go install something, you’ll be back here. Bookmark this page.


PATH cheat sheet

You made it. Or you skipped straight here. Either way, no judgment. Copy and paste these. Pin them. Tattoo them on your forearm. Whatever works.

What you’re doing Command
Show your PATH echo $PATH | tr ':' '\n'
Find a command which python3
Find all versions which -a python3
Find installed binary anywhere find / -name "cmd" -type f 2>/dev/null
Add to PATH (in ~/.bashrc) export PATH="$HOME/.local/bin:$PATH"
Reload after changes source ~/.bashrc
Clear command cache hash -r
Check system PATH cat /etc/environment

Common directories to add:

Ecosystem Add to ~/.bashrc
Python (pip –user) export PATH="$HOME/.local/bin:$PATH"
Go export PATH="$HOME/go/bin:$PATH"
Rust (cargo) export PATH="$HOME/.cargo/bin:$PATH"
Go (system install) export PATH="/usr/local/go/bin:$PATH"

The fix for 90% of “command not found”: export PATH="$HOME/.local/bin:$PATH" in your ~/.bashrc, then source ~/.bashrc.

Back to the top, you overachiever.