Environment Variables and PATH
Environment variables are named values held in a process's environment that configure its behavior — things like HOME (your home directory), USER (your username), LANG (locale settings), and PATH (where to look for executables). Every process has its own copy of the environment, inherited from its parent at the moment it was launched (fork/exec), and a child can see and use its parent's exported variables but cannot change the parent's copy — environment changes only flow downward, never back up. This one-way inheritance model explains a lot of shell scripting behavior that confuses beginners, such as why setting a variable inside a script and then returning to the interactive shell doesn't leave that variable set there.
Cricket analogy: Environment variables inherited one-way from parent to child process are like a captain's tactical instructions passed down to fielders at the start of an innings — a fielder can follow the plan but can't retroactively change the captain's original strategy once play has begun.
Shell Variables vs Environment Variables
A plain assignment like FOO=bar creates a shell variable visible only within the current shell — it is NOT automatically passed to child processes. Running export FOO=bar (or export FOO after assigning it) marks the variable for export, meaning it becomes part of the environment handed to every child process spawned afterward. env or printenv list the current environment (exported variables only); set (with no arguments, in Bash) lists all shell variables, functions, and exported variables together. This distinction trips up many scripts: forgetting export means a variable set in one script is invisible to any command or subprocess that script calls.
Cricket analogy: A plain FOO=bar assignment staying local is like a batsman privately noting a bowler's tell in their own head, useless to teammates, while export FOO=bar is like radioing that tell to the whole team's earpieces so every fielder downstream can act on it.
# Shell variable only — NOT visible to child processes
FOO=bar
bash -c 'echo "child sees FOO=$FOO"' # prints empty
# Exported — visible to child processes
export FOO=bar
bash -c 'echo "child sees FOO=$FOO"' # prints bar
# View all exported environment variables
env | sort
# View one specific variable
printenv HOME
# Set a variable only for a single command's environment (not persistent)
DEBUG=1 ./run-tests.sh
# Unset a variable entirely
unset FOOHow PATH Resolves Commands
PATH is a colon-separated list of directories the shell searches, in order, when you type a bare command name like python3 or git without a path prefix. The shell checks each directory left to right and runs the first matching executable it finds — this is why having two versions of a tool installed (e.g. a system Python and a pyenv-managed Python) can silently resolve to the 'wrong' one if PATH ordering isn't what you expect. Use which <command> or the more thorough type -a <command> to see exactly which executable (and, with type -a, every matching executable across all of PATH plus any shell builtins/aliases with the same name) would run.
Cricket analogy: PATH searching directories left to right for the first match is like an umpire checking review evidence sources in a fixed priority order — ball-tracking first, then snickometer — and stopping at the first conclusive result, which is why type -a listing every available check matters when results conflict.
echo $PATH
# /home/user/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# Prepend a directory so it's checked FIRST (highest priority)
export PATH="$HOME/bin:$PATH"
# Append a directory so it's checked LAST (lowest priority)
export PATH="$PATH:/opt/myapp/bin"
# Show which executable will actually run
which python3
type -a python3
# Run a specific executable directly, bypassing PATH search entirely
/usr/bin/python3 --versionPATH lookup is skipped entirely whenever a command name contains a slash — running ./script.sh or /usr/bin/python3 executes that exact file directly. This is precisely why the current directory (.) is deliberately NOT included in PATH by default on Linux/Unix: if it were, running ls in a directory containing a malicious file named ls could execute an attacker's script instead of the real /bin/ls. This differs from historical DOS/Windows behavior, where the current directory was implicitly searched first — a design now recognized as a security liability.
A dangerously common mistake is appending an untrusted or writable directory to the FRONT of PATH (e.g. export PATH=/tmp:$PATH), which lets anyone who can write to that directory shadow system commands with malicious versions that your shell — and any scripts you run — will silently execute instead. Similarly, cron jobs and systemd services run with a minimal PATH (often just /usr/bin:/bin) that does NOT match your interactive shell's PATH, which is a frequent cause of 'works when I run it manually, fails in cron' bugs — always use absolute paths or explicitly set PATH inside scheduled scripts.
Where Variables Are Set Persistently
For a variable (or PATH addition) to survive across sessions, it must be set in a shell startup file, not just typed interactively. Bash reads different files depending on whether the shell is a login shell (/etc/profile, then ~/.bash_profile or ~/.bash_login or ~/.profile — the first one found) or an interactive non-login shell (~/.bashrc). System-wide defaults for all users typically live in /etc/environment (a simple KEY=value file, no shell syntax, read by PAM at login) and /etc/profile.d/*.sh. Getting this placement wrong is the second most common source of 'my PATH change didn't take effect' confusion, after forgetting to export the variable in the first place.
Cricket analogy: Setting PATH in the wrong startup file is like a team announcing a new batting order only in the dressing room team-talk (interactive shell) but never updating the official team sheet filed with the match referee (login shell config), so the change doesn't take effect at the actual toss.
- A plain FOO=bar is a shell variable local to the current shell;
export FOO=barpromotes it into the environment inherited by all child processes. - Environment inheritance is one-way (parent to child) — a child process can never modify its parent's environment.
- PATH is a colon-separated, order-sensitive list of directories searched for bare command names; the first match wins.
- Prepend to PATH to give a directory priority; append to make it a fallback; use
whichortype -ato debug resolution. - The current directory is intentionally excluded from PATH by default as a security measure against command-shadowing attacks.
- cron and systemd run with a minimal PATH that differs from your interactive shell — always use absolute paths or set PATH explicitly in scheduled scripts.
Practice what you learned
1. What is the key difference between `FOO=bar` and `export FOO=bar` in Bash?
2. When you type `git status` at the prompt, how does the shell decide which `git` executable to run?
3. Why is the current directory (.) deliberately not included in PATH by default on Linux?
4. A script works fine when run manually but fails with 'command not found' when triggered by cron. What is the most likely cause?
5. What does running `./myscript.sh` do differently from running `myscript.sh` with respect to PATH?
Was this page helpful?
You May Also Like
The Shell and Terminal Explained
Clarifies the difference between the terminal, the terminal emulator, the shell, and the kernel, and introduces Bash as the default command interpreter on most Linux systems.
Writing Your First Bash Script
Get hands-on with the anatomy of a Bash script — shebang, permissions, execution, comments, and structuring commands into a reusable, repeatable tool.
Variables and Quoting in Bash
Understand how Bash stores and expands variables, and why quoting rules — single, double, and unquoted — are critical to writing scripts that don't break on real-world input.
Scheduling Tasks with cron
Learn crontab syntax for scheduling recurring jobs, the difference between user and system crontabs, and common pitfalls like PATH and logging.
Related Reading
Related Study Notes in DevOps
Browse all study notesNginx Study Notes
DevOps · 30 topics
DevOpsAnsible Study Notes
DevOps · 30 topics
DevOpsAdvanced Kubernetes Study Notes
Kubernetes · 30 topics
DevOpsAdvanced Bash Scripting Study Notes
Bash · 30 topics
DevOpsApache Kafka Study Notes
Kafka · 30 topics
DevOpsDocker & Kubernetes Study Notes
YAML · 40 topics