A shell variable only exists in the shell that created it. An environment variable is a shell variable that's been marked for export, meaning every child process the shell starts gets a copy of it. That one distinction explains most of the confusion around variables "not working" in a script or a program launched from the shell.

Shell variable vs. environment variable

MY_VAR="hello"        # shell variable - visible only in this shell
echo "$MY_VAR"        # hello
bash -c 'echo $MY_VAR' # (nothing - the child shell never saw it)

export MY_VAR="hello"  # now it's an environment variable
bash -c 'echo $MY_VAR' # hello

export is the whole mechanism. Anything exported before a child process starts is inherited by it; anything not exported is invisible outside the shell that set it. This is one-directional - a child process can't set an environment variable that its parent shell will see afterward, since the child only ever has its own copy.

Variables you'll actually use

Variable Purpose
PATH Colon-separated directories searched, in order, for executables
HOME The current user's home directory
USER The current username
SHELL The user's default login shell
EDITOR The editor invoked by tools like crontab -e, git commit
LANG / LC_* Locale settings - affects sorting, date formats, error message language

Inspecting the environment

printenv PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
env | sort

lists every exported variable a process would inherit. printenv and env with no arguments do almost the same thing; env can also run a command with a modified environment (env VAR=value command), which is handy for a one-off override without polluting the current shell.

Setting variables permanently

Where to put an export line depends on scope:

  • ~/.bashrc - for variables needed in every interactive shell (most terminal tabs you open)
  • ~/.profile or ~/.bash_profile - for variables needed at login, including in graphical sessions that don't necessarily read .bashrc
  • /etc/environment - system-wide, applied at login for all users; unlike the files above, this one is KEY=value pairs only, not a shell script, so no export keyword and no command substitution
  • A .env file loaded by a specific application or docker compose - scoped to that tool, not the shell at large

After editing ~/.bashrc, reload it in the current shell with source ~/.bashrc rather than opening a new terminal every time - see Bash Basics.

Extending PATH instead of replacing it

A common mistake is overwriting PATH instead of adding to it:

export PATH="$HOME/bin:$PATH"   # correct: prepend, keep the rest
export PATH="$HOME/bin"         # wrong: every other command now "not found"

Using variables in scripts

Environment variables are how scripts read configuration without hardcoding it - see Shell Scripting for reading them safely (including handling the case where one isn't set), and Sudo and Privilege for why sudo strips most environment variables by default rather than passing your shell's environment through to a root-owned process.