Fixing Linux 'command not found' (ls·vi·vim) — Restoring the PATH

The Problem

On Linux-family OSes, mishandling environment variables can leave basic commands like ls and vi unrecognized.

# macOS (zsh)
zsh: command not found: ls

# Ubuntu (bash)
bash: ls: command not found

This post looks at how this happens and how to fix it. (If PATH/environment variables are new to you, the Windows PATH post below is a good companion read.)

Installing Python on Windows + Setting the PATH — When 'python' Is Not Recognized
Install Python on Windows and register the install path and the Scripts (pip) path in the system PATH so that just typing 'python' or 'pip' works in cmd. Covers the difference between user and system variables and the site-packages structure of pip-installed libraries.
taystudios.com/blog

macOS is also a Linux-family OS, so the same fix applies to other Linux distros (Ubuntu, CentOS). Here I'll explain using macOS (zsh).

1. Setting environment variables — what caused the problem

# Home directory (prompt starts with ~)
thlee@MacBookAir ~ % ls -ial

# Result (partial)
-rw-------  1 thlee  staff  25246 .zsh_history
drwx------ 27 thlee  staff    864 .zsh_sessions
-rw-r--r--  1 thlee  staff    314 .zshrc

On Linux-family systems, environment variables are written in each shell's rc file (run commands). This file is hidden (leading .). zsh uses .zshrc, bash uses .bashrc.

thlee@MacBookAir ~ % vim ~/.zshrc

Open .zshrc in the home directory (~) with vim.

Setting the Python path with export PATH in .zshrc Figure 1. Setting the environment variable

export PATH="/Library/Frameworks/Python.framework/Versions/3.11/bin"

As in Figure 1, I wanted to set the Python environment variable, so I set PATH like above, then ran source to apply it.

thlee@MacBookAir ~ % source ~/.zshrc
thlee@MacBookAir ~ % python3
Python 3.11.6 (...) on darwin
>>>

Typing python3 launched the Python interpreter — the python3 executable at /Library/Frameworks/Python.framework/Versions/3.11/bin worked.

2. command not found appears

But from this point, ls and vim stopped working.

thlee@MacBookAir ~ % ls
zsh: command not found: ls
thlee@MacBookAir ~ % vim ~/.zshrc
zsh: command not found: vim

Now I can't even re-edit the environment variables in .zshrc/.bashrc.

Why did it happen?

List of executables like ls, pwd, kill in the /bin directory Figure 2. Executables like ls, pwd, kill in /bin

ls, vim, vi, kill, etc. are all executables. They ran from just typing ls only because the OS had their path in PATH.

But applying export PATH="/Library/.../3.11/bin" overwrote PATH entirely. Now ls, vi, and vim can't be found. So we just need to fix PATH again.

3. The Fix

If I can't get into .zshrc, how do I fix the path?

echo is a shell built-in and works regardless of PATH. Let's check the current PATH.

echo $PATH
/Library/Frameworks/Python.framework/Versions/3.11/bin

As expected, only the Python path remains. Re-add the base paths with export.

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

Using : as a separator, we add the 5 paths /usr/local/bin, /usr/bin, /bin, /usr/sbin, /sbin so the base programs work again. (e.g., ls lives in /bin, mount-related commands in /sbin.)

thlee@MacBookAir ~ % export PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$PATH"
thlee@MacBookAir ~ % echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Frameworks/Python.framework/Versions/3.11/bin

Notice the previous Python path is still there at the end. This is where the trailing :$PATH shows its role.

The meaning of :$PATH (important)

Most of the time you want to add a new path while keeping the existing ones. Setting a single path that wipes the existing PATH would be inconvenient, so paths are chained with :. The $ references the current environment variable value, so $PATH pulls in the existing PATH.

"{new path}:$PATH"  =  "{new path}:{existing path}"

That is, it keeps the existing paths at the end while adding the new path in front.

ls working again after restoring PATH Figure 3. The restored PATH

Now ls works again. But this is a temporary fix that applies only to the current shell session. You must go into .zshrc/.bashrc and fix the setting properly. If you don't and source again, the base paths vanish once more.

Quick Summary

If you don't need the explanation and just want the result, enter the following in the shell to restore the vanished base-command paths at the front.

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

Then, in .zshrc/.bashrc, append :$PATH after any path you want to add.

.zshrc finally set in the new-path:$PATH form Figure 4. Adding the existing path (final)

From now on, whenever you add a path to PATH, always include $PATH. It doesn't have to be at the end.

"$PATH:{new path}"  =  "{existing path}:{new path}"

Putting it in front also works; only the search order changes. Still, appending is recommended — the OS searches PATH entries in order, so the further back base commands are, the slower the lookup can be.


See also the Windows counterpart (CMD drive change) in the same OS-settings category.

How to Switch to the D Drive in Windows CMD (When cd Doesn't Work)
In Windows CMD, cd can't move to another drive. Why you must type 'D:' to go from C: to the D drive — the Windows filesystem with an independent root per drive vs Linux's single root (/).
taystudios.com/blog

📦 Migrated from my own Korean blog (my own writing). Original: taehyuklee.tistory.com/22

Share𝕏f

Comments