I know how annoying it is to type a command you've run a hundred times and get slapped with bash: command not found.
Here's the fastest way to fix it. First, check if the binary actually exists:
which mkdir
ls /usr/bin/mkdir
ls /usr/local/bin/mkdir
If which returns nothing but the file exists on disk, your $PATH is broken. Print it:
echo $PATH
You should see something like /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin. If /usr/bin or /usr/local/bin is missing, that's your problem. Fix it for the current session:
export PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$PATH"
Make it permanent by editing the right startup file. On most Linux distros it's ~/.bashrc. On macOS with zsh (the default since Catalina), it's ~/.zshrc. Don't edit both — pick the one your shell actually reads.
echo 'export PATH="/usr/local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
If which also fails, the binary is genuinely missing. Install it. On Debian/Ubuntu:
sudo apt update && sudo apt install <package-name>
On Fedora/RHEL: sudo dnf install <package-name>. On macOS with Homebrew: brew install <package-name>. If you're not sure which package ships the command, search for it:
apt-file search bin/mkdir
# or on macOS:
brew search mkdir
Why this actually works
Every time you hit Enter, bash walks through each directory in $PATH left to right, looking for an executable with that name. If nothing matches, you get command not found. It's never about the command being 'broken' — it's about the shell not knowing where to look, or the file not existing.
Two things trip people up here. First, order matters. If two versions of python live in different PATH entries, the one listed first wins. That's why tools like pyenv and nvm tell you to prepend their bin directory. Second, startup files are shell-specific. Running source ~/.bashrc in a zsh terminal does nothing useful because zsh never sourced it in the first place.
A quick sanity check — if ./myscript.sh works but myscript.sh doesn't, your current directory isn't on the PATH (and on modern systems, it shouldn't be). That's not a bug, it's a security feature.
Less common variations
It works in your terminal but not in cron or systemd
Systemd services and cron jobs run with a minimal environment. They don't read ~/.bashrc. If your script says command not found only when scheduled, hardcode the full path or set PATH inside the unit file:
[Service]
Environment="PATH=/usr/local/bin:/usr/bin:/bin"
It broke right after you ran a docker exec or sudo -i
sudo -i loads root's environment, not yours. Your custom PATH entries are gone until you exit. Same story with su -. If you want your PATH preserved, use sudo -E or add the path to /etc/environment.
You installed it but bash still can't see it
Ten to one, you're on a shell where hash -r needs to run, or you installed it into ~/.local/bin which many distros don't add to PATH by default. Check with ls ~/.local/bin and add it manually.
Pro tip: ifwhichis silent buttype -ashows a shell function or alias, the command isn't missing — it's shadowed. Rununalias <name>orunset -f <name>.
Prevention
- Keep
~/.bashrc(or~/.zshrc) tidy. Append, don't overwrite, and comment each PATH line with a date and reason. - Never put
.or your current directory on the PATH. It's a security hole and a bad habit. - After editing startup files, run
sourceon the correct file for your shell, then open a fresh terminal to verify. - Use
command -vinstead ofwhichin scripts. It's POSIX-compliant and doesn't lie when aliases are involved. - For tools like nvm, pyenv, rbenv, and cargo, follow their install instructions exactly — they usually want their shim directory prepended, not appended.
That's it. Nine times out of ten it's a PATH edit or a missing package, and you'll be back to typing commands in under a minute.