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.)
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.
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?
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.
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.
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.
📦 Migrated from my own Korean blog (my own writing). Original: taehyuklee.tistory.com/22


Comments