When you type nginx or node, the shell doesn’t search the whole disk. It looks through the directories listed in the PATH environment variable, in order, and runs the first match. Most “command not found” problems after installing software come down to PATH.

See your PATH

echo "$PATH"
# /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin

echo "$PATH" | tr ':' '\n'      # one directory per line

Directories are separated by :. Order matters: /usr/local/bin comes before /usr/bin, so a locally installed version wins over the distribution’s.

Find where a command comes from

command -v node        # path of the command that will run
type -a python3        # all matches, plus aliases and functions
which -a php

type -a is the most complete. It shows aliases and shell functions too, which can shadow real commands.

The shell caches command locations. After installing or moving a binary in the same session, run hash -r to clear the cache.

Add a directory for this session

export PATH="$HOME/.local/bin:$PATH"     # prepend: takes priority
export PATH="$PATH:/opt/tool/bin"        # append: used only if nothing earlier matches

This lasts until you close the shell.

Add a directory permanently

Just for your user. Add the line to ~/.bashrc (interactive shells) or ~/.profile (login shells):

echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

Use single quotes so $PATH is written literally and expanded at login, not now.

Many distributions’ default ~/.profile already adds ~/bin and ~/.local/bin if they exist. Tools installed with pip install --user, pipx or Cargo go to ~/.local/bin or ~/.cargo/bin.

For all users. Create a file in /etc/profile.d/:

echo 'export PATH="$PATH:/opt/tool/bin"' | sudo tee /etc/profile.d/tool.sh

It applies at the next login. Or simply symlink the binary into a directory that’s already on the PATH:

sudo ln -s /opt/tool/bin/tool /usr/local/bin/tool

That’s often the cleanest option. /usr/local/bin exists for exactly this. See ln.

Avoid editing /etc/environment with shell syntax. It’s read by PAM, not a shell, so it takes plain KEY="value" lines and no $PATH expansion.

PATH and sudo

sudo mytool        # sudo: mytool: command not found

sudo uses its own safe PATH (secure_path in /etc/sudoers), not yours. Options:

  • Install the tool in /usr/local/bin (it’s in secure_path).
  • Use the full path: sudo /opt/tool/bin/mytool or sudo "$(command -v mytool)".
  • Add the directory to secure_path with visudo, carefully.

PATH in cron and systemd

Cron runs with a minimal PATH, often just /usr/bin:/bin. Commands in /usr/local/bin or ~/.local/bin aren’t found. Set PATH at the top of the crontab:

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

or use absolute paths in jobs. See crontab.

systemd services get a default PATH too, and don’t read your .bashrc. Use absolute paths in ExecStart= or set Environment=PATH=….

Version managers such as nvm, pyenv and rbenv change PATH in your shell startup files. Scripts run by cron or systemd don’t see them, so point to the exact binary (/home/app/.nvm/versions/node/v22…/bin/node) or install system-wide. See nvm.

Security notes

  • Never put . (the current directory) in PATH, especially for root. A malicious ls in a shared directory would run when you cd there.
  • Keep writable directories out of root’s PATH.
  • Be aware that a directory earlier in PATH can shadow system commands. If python3 suddenly behaves oddly, run type -a python3.

Fix “command not found”

  1. Is it installed? Check dpkg -L package | grep bin/ or rpm -ql package.
  2. Where is it? Search with sudo find / -name toolname -type f 2>/dev/null.
  3. Is that directory in echo $PATH? If not, add it or symlink the binary.
  4. Opened a new shell? Run source ~/.bashrc or hash -r.
  5. Under sudo, cron or systemd? See the sections above.

Quick reference

Task Command
Show PATH echo "$PATH" | tr ':' '\n'
Where is a command command -v x / type -a x
Add for session export PATH="/dir:$PATH"
Add for user line in ~/.bashrc or ~/.profile
Add for everyone file in /etc/profile.d/
Simplest ln -s /opt/x/bin/x /usr/local/bin/x
Clear lookup cache hash -r

Related: environment variables and shebang and running scripts.

  • Linux
  • Bash
  • Environment