LearnLinux FoundationsBash Shell Environment

The $PATH Variable

PATH - a hands-on Linux lab on a real virtual machine.

PATH is the colon-separated list of directories the shell searches, left to right, to find the program for a bare command name. Read it with echo $PATH, understand why the first match wins, decode command not found as a missing directory rather than a missing program, and add your own directory by appending with export PATH="$PATH:$HOME/bin" (searched last) or prepending (searched first). PATH is machine-specific, so the drills teach the shape and the moves; your directory list will differ.

You type ls and press Enter. A program runs and lists your files. But ls is not a spell the shell knows by heart. It is a real program, a file sitting in a directory somewhere on the disk, and the shell had to FIND that file before it could run it.

So how did it know where to look? You typed three letters, not a full address like /usr/bin/ls. Something quietly translated the short name into the right file on disk. That something is a single variable, and once you understand it, half of the shell's mysteries, including the dreaded command not found, stop being mysteries. It is called PATH.

The black boxes in this lesson are a practice terminal: a safe sandbox that checks the one command each step teaches, so nothing you type can break anything. PATH holds real directories from a real machine, so its exact list changes from box to box. Wherever you see echo $PATH, the value shown is one real capture kept as an example; yours will read differently, and that is marked where it appears.

PATH is an environment variable, one of the exported settings you met in the last lessons, that holds a list of directories. When you type a command by its bare name, the shell walks that list from left to right. It looks in each directory for a program with that name, and runs the FIRST one it finds.

Think of it as the shell's address book. You say a short name, ls, and the shell checks each address in PATH in order until it finds a program called ls. The directories are strung together with colons, :, one after another, into a single line. That colon-separated list IS the search path, and it is why typing ls works while typing a name that lives nowhere in PATH fails.

This search-a-list-of-directories idea is as old as Unix. It is why you can install a program into one of the listed directories and immediately run it by name, no full address required. Every command you have typed in this whole track, ls, cd, echo, df, was found this exact way: the shell looked it up in PATH and ran the first match.

You can read the list yourself. PATH is just a variable, so you print it the same way you print any variable: echo $PATH. The $ tells the shell to substitute the variable's value, and echo prints that value back to you.

Run it now and look at the colon-separated directories:

echo $PATH

prompt: student@linuxcamp:~$ answer: echo $PATH output: /usr/local/bin:/usr/bin:/bin:/usr/local/games hint: A dollar sign in front of the name prints its value: echo $PATH

That exact list belongs to the machine this was captured on. On YOUR machine the directories, their order, and how many there are will all be different: you might see /usr/local/sbin and /sbin too, or on a stripped-down container just /usr/bin:/bin. What stays the same everywhere is the SHAPE: a run of real directories joined by colons, searched left to right. That shape is the lesson, not the specific list.

The order of the directories is not decoration. It is a priority ranking. The shell stops at the FIRST program it finds with the name you typed, so a directory earlier in the list beats a directory later in the list.

Read the captured list again, left to right: /usr/local/bin, then /usr/bin, then /bin, then /usr/local/games. When you type ls, the shell looks in /usr/local/bin first. No ls there, so it tries /usr/bin. It finds ls there and runs it, and never even looks in /bin. If there were TWO programs named ls, one in /usr/local/bin and one in /usr/bin, the one in /usr/local/bin would win because it comes first.

You already have the tool that reveals the winner. which NAME, from an earlier lesson, prints the full path of the program the shell would actually run, which is the first match in PATH. When two versions of a command confuse you, which tells you which directory won.

Now the payoff. When you type a name and the shell walks the entire PATH and finds no program with that name in ANY of those directories, it gives up with one specific message. Type a name that is not a real command and watch it happen.

Before you run it, commit to an answer: what does the shell say when it cannot find mytool anywhere in PATH?

mytool

prompt: student@linuxcamp:~$ answer: mytool output: bash: mytool: command not found hint: Just type the made-up name and press Enter: mytool

bash: mytool: command not found. Read that line for what it really means. It is NOT saying the program is broken or missing from the disk. It is saying the shell searched every directory in PATH, left to right, and none of them held a program named mytool. That is almost always what command not found means: not that the program is gone, but that its directory is not on your search path. What to check first: run echo $PATH and ask whether the program's directory is even in the list.

So how do you make a program of your own runnable by its bare name? You add its directory to PATH. The safe, everyday way is to APPEND your directory to the end of the existing list, keeping everything already there.

The form is export PATH="$PATH:$HOME/bin". Read it right to left inside the quotes: $PATH is the whole current list, then a colon separator, then $HOME/bin, your personal bin directory. So you are saying: keep the entire existing path, then tack my directory on the end. export, from the last lesson, makes the new value stick for this shell and the programs it launches.

Run the append. The shell simply gives you the prompt back, no output, which means it worked:

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

prompt: student@linuxcamp:~$ answer: export PATH="$PATH:$HOME/bin" output: hint: Keep the old path, add a colon, then your bin: export PATH="$PATH:$HOME/bin"

No output, just the prompt returning. That silence is success: export quietly rebuilt PATH as the old list plus your new directory on the end. If you print it now with echo $PATH, your bin directory appears last, after everything that was there before. Because you appended, your directory is searched LAST: the shell only reaches it if no earlier directory held the program. On the capture machine the result looks like this, and your own list will differ but the new tail is the same idea:

prompt: student@linuxcamp:~$ answer: echo $PATH output: /usr/local/bin:/usr/bin:/bin:/usr/local/games:/home/student/bin hint: Same print as before, to see where your directory landed: echo $PATH

Again, everything before the last colon is this machine's own list and yours will differ. The one part to read is the tail: /home/student/bin now sits at the very end, exactly where the append put it. That is $HOME/bin after the shell expanded $HOME to your home directory. Appended means searched last.

There are two places you can add a directory, and the choice decides who wins a name clash. You just APPENDED, putting your directory last. The mirror image is to PREPEND, putting your directory FIRST.

export PATH="$HOME/bin:$PATH" puts $HOME/bin at the front, before the whole existing list. Now the shell looks in your directory FIRST, so if you have your own ls in $HOME/bin, it wins over the system ls. That is the difference in one sentence: append and your copy is searched last as a fallback, prepend and your copy is searched first and overrides. Same two pieces, $PATH and $HOME/bin, and the only thing that changed is which side of the colon each one sits on.

Prepending is powerful and easy to misuse. Put a directory first and any program in it silently shadows the real system command of the same name, which can surprise you badly later. Most of the time you want the safe append form, export PATH="$PATH:$HOME/bin", so your additions are a fallback and never quietly replace a system tool. Prepend only when you deliberately mean to override.

Here is the gotcha that catches everyone once. You write a script called myscript, it sits right there in your current directory, you type myscript, and the shell says command not found. The file is RIGHT THERE. Why?

Because your current directory is not in PATH, and for safety it almost never is. The shell only searches the directories in PATH, and "the folder I happen to be standing in" is deliberately not one of them. So a bare name never finds a program in your current directory.

The fix is to give the shell the program's address directly instead of asking it to search. ./myscript means "the file named myscript in this exact directory," where . is shorthand for the current directory. A full address like /opt/tool/bin/tool works the same way: when you hand the shell a path with a slash in it, it skips the PATH search entirely and runs exactly that file.

The rule is simple: a name with NO slash gets looked up in PATH; a name WITH a slash, like ./myscript or /opt/tool/bin/tool, is run directly as that exact file, no search. That is why running a program in your current directory needs the ./ in front. It is not PATH at all; it is an address.

Scaffolding off. No command is printed this time. You have every piece you need.

You have built some scripts and dropped them in your personal bin directory, $HOME/bin. You want to run them by their bare names, but you do NOT want them to override any system command, so they should be searched LAST. Add $HOME/bin to the end of your existing PATH, keeping everything already there, and make the change stick for the programs you launch. Build the whole line yourself.

prompt: student@linuxcamp:~$ answer: export PATH="$PATH:$HOME/bin" output: hint: You want the OLD path first, then a colon, then your bin, all exported. Think export PATH="$PATH ... $HOME/bin".

No output, just the prompt, which is exactly right: the append succeeded silently. You put $PATH first to keep the whole existing list, added a colon, then $HOME/bin on the end, so your directory is searched LAST and can never shadow a system command. Wrapping it in export made it reach the programs you run. That right-to-left reading, old path, colon, new directory, is the everyday move for adding to your search path, and you built it from memory.

You earned this cheat sheet. Every row is a form you just ran or met:

The one idea to carry out: command not found almost never means the program is gone. It means its directory is not on PATH. You either add that directory to PATH, or run the program by a full address with a slash in it. First match, left to right, wins.

A change to PATH lasts only for the current shell and the programs it launches. Close the terminal and your added directory is gone. To make a directory join PATH automatically every time you open a shell, you put the export PATH=... line in a startup file called .bashrc. That file, and how the shell reads it at every login, is a lesson of its own coming later in this module.

The practice terminal has shown you the whole story of PATH. You read your search path and saw that the first match left to right wins. You decoded what command not found is really telling you, and added a directory of your own, both as a safe append and an overriding prepend. You learned the one case where PATH does not apply, running a program by its address with ./. Every one of those you typed yourself.

The Bash Environment module ends with one real Linux machine, the Bash Environment capstone mission, and that is where you bend PATH for real. A VM boots just for you, with its own live search path full of real directories you can read, append to, and hunt commands through. It hands you objectives that use exactly what you practiced across this module: variables, env, export, and the PATH they all flow through. One difference from here: the mission shows no commands. You read the objective, you recall the command, you type it. That recall is what makes it stick.

Finish the other Bash Environment lessons, then go add a directory to a real search path for yourself.

Practice The $PATH Variable in a real Linux terminal at The Linux Camp. Progress is verified automatically as you type commands on the machine.