Environment Variables and PATH in Bash: export, env and Startup Files

Basics · 7 min read · Updated 25 Oct 2026

On this page
  1. Viewing variables
  2. Shell variables vs environment variables
  3. Set a variable for one command
  4. How PATH works
  5. Add a directory to PATH
  6. Make changes permanent
  7. Load a .env file
  8. Why cron and sudo see a different environment
  9. Quick reference

Every process on Linux carries a small set of named values called environment variables. They tell programs where your home directory is, which editor you prefer, what language to use and, most importantly, where to look for commands. When a command works in one terminal but not in another, or works for you but not in cron or under sudo, the environment is usually the reason.

Viewing variables

$ echo "$HOME"
/home/alice
$ printenv HOME
/home/alice
$ printenv | less        # everything in the environment
$ env | grep -i lang     # search it

printenv and env show only exported environment variables. Bash also has ordinary shell variables that are not passed to other programs; set with no arguments lists both, plus all your functions, which is why its output is so long.

Some variables you will meet often:

VariableHolds
HOMEYour home directory
USERYour user name
SHELLYour login shell
PATHDirectories searched for commands
EDITOR / VISUALEditor used by git, crontab and others
LANGLanguage and character encoding
PWDCurrent directory
PS1Your prompt (a shell variable, usually not exported)

Shell variables vs environment variables

An assignment creates a shell variable that only the current shell can see. export marks it to be copied into the environment of every program the shell starts:

$ GREETING="hi"
$ bash -c 'echo "child sees: [$GREETING]"'
child sees: []
$ export GREETING
$ bash -c 'echo "child sees: [$GREETING]"'
child sees: [hi]

You can assign and export in one step with export NAME=value. Remove a variable with unset NAME. Note that there are no spaces around =: NAME = value tries to run a command called NAME.

The copy only goes one way. A child process gets its own copy of the environment and cannot change the parent's. That is why running a script that sets variables does nothing to your terminal, while source script.sh (or . script.sh) runs it in the current shell and does.

Set a variable for one command

Put the assignment in front of the command. It applies to that command only and leaves your shell untouched:

$ FOO=bar bash -c 'echo "$FOO"'
bar
$ echo "after: [$FOO]"
after: []

This is the clean way to change behaviour for a single run, for example LC_ALL=C sort file for byte-order sorting, or TZ=UTC date to see the time in UTC. env -i command goes further and runs a command with an empty environment, which is useful for testing whether a script depends on something in yours.

How PATH works

PATH is a colon-separated list of directories. When you type a command name without a slash, Bash checks each directory in order and runs the first match. To see the list one per line:

$ echo "$PATH" | tr ':' '\n'
/usr/local/bin
/usr/bin
/bin

Order matters. Something in /usr/local/bin wins over a program of the same name in /usr/bin, which is how locally installed versions override system ones. To see which one Bash will run, and every candidate:

$ type mytool
mytool is /home/alice/bin/mytool
$ type -a ls
ls is /usr/bin/ls
ls is /bin/ls
$ type cd
cd is a shell builtin

type also reports aliases, functions and builtins, which which cannot see. In scripts, command -v name is the portable way to check whether a command exists.

Add a directory to PATH

A personal ~/bin or ~/.local/bin directory is the usual home for your own scripts. Make a script executable and it still will not run by name until its directory is in PATH:

$ mkdir -p ~/bin
$ printf '#!/bin/bash\necho "hello from mytool"\n' > ~/bin/mytool
$ chmod +x ~/bin/mytool
$ mytool
bash: mytool: command not found
$ export PATH="$HOME/bin:$PATH"
$ mytool
hello from mytool

Always include the old value with $PATH. Writing PATH=~/bin on its own replaces the whole list, and suddenly ls and every other command stop working in that terminal. If that happens, just open a new terminal.

Putting your directory first lets your scripts override system commands; putting it last (PATH="$PATH:$HOME/bin") does the opposite. If Bash keeps running an old location after you move a program, run hash -r to clear its lookup cache.

Never put . (the current directory) in PATH, especially for root. Anyone who can write a file called ls into a directory you visit could get you to run it.

Make changes permanent

Variables set at the prompt disappear when the terminal closes. To keep them, add the lines to a startup file. Which file depends on the kind of shell:

  • ~/.bashrc is read by interactive non-login shells, which is what most terminal windows open. Aliases, functions and your prompt belong here.
  • ~/.bash_profile (or ~/.profile if that does not exist) is read by login shells, such as an SSH session or a console login. Most distributions make it source ~/.bashrc too.
  • /etc/environment and files in /etc/profile.d/ apply to all users and need root.

For PATH and exported variables, ~/.profile or ~/.bash_profile is the traditional place, but putting the line in ~/.bashrc works in practice on most desktops and servers. Guard against adding the same directory again every time the file is re-read:

case ":$PATH:" in
  *":$HOME/bin:"*) ;;
  *) export PATH="$HOME/bin:$PATH" ;;
esac

After editing, run source ~/.bashrc or open a new terminal. The .bashrc guide covers the rest of that file, and the prompt generator builds a PS1 line to add to it.

Load a .env file

Many projects keep settings in a .env file of simple KEY=value lines. To load one into your shell and export every variable, turn on automatic export while sourcing it:

$ cat .env.sample
APP_ENV=dev
APP_PORT=8080
$ set -a; source .env.sample; set +a
$ bash -c 'echo "$APP_ENV:$APP_PORT"'
dev:8080

set -a exports every variable assigned until set +a. Only do this with files you trust, since sourcing runs the file as shell code. Values with spaces must be quoted inside the file.

Environment variables are a common way to pass secrets such as API keys, but they are not private. Other processes run by the same user, and root, can read them from /proc/PID/environ, and they can end up in crash reports. Keep secrets in files with tight permissions where you can.

Why cron and sudo see a different environment

Cron runs jobs with a tiny environment and a minimal PATH, and it does not read your .bashrc. A script that works at the prompt can fail in cron with "command not found". Use full paths or set PATH at the top of the crontab; the cron guide shows how.

sudo also resets most of the environment and usually replaces PATH with a fixed secure_path. Use sudo env VAR=value command to pass a specific variable, or sudo -E to keep yours where the sudoers policy allows it.

Quick reference

GoalCommand
Show one variableprintenv HOME
Show all exportedenv
Set and exportexport NAME=value
Removeunset NAME
For one commandNAME=value command
PATH one per lineecho "$PATH" | tr ':' '\n'
Prepend to PATHexport PATH="$HOME/bin:$PATH"
Which command runstype -a name
Reload startup filesource ~/.bashrc
Load a .env fileset -a; source .env; set +a