Bash Environment Variables: Set, Export, List and Unset

Tested on RHEL 10.2 (Coughlan)
Package bash 5.2.26-6.el10
Applies to Linux distributions with Bash installed
Privilege Normal user; no root required for the examples
Scope Shell variables versus exported environment variables, export, export -p, export -n, unset, env, printenv, one-command VAR=value command, env -u, env -i, PATH usage, and parent/child inheritance. Does not cover startup-file placement for persistent variables in depth.
Related guides Bash shell script tutorial
Shell scripting course hub
Short on time?Beta Get the gist of this 10-minute read in seconds, then ask follow-up questions

Bash environment variables let a shell pass configuration values to programs and child shells that it starts. You can set a variable in your current session, export it for programs and child shells started later, or pass a value to a single command without changing your current shell.

External programs and child shells receive environment variables from the shell that starts them. They get their own environment when they start, so later changes inside the child do not update the parent shell.


Quick Reference

Task Command
Set shell variable VAR=value
Export variable export VAR=value
Export existing variable export VAR
Print one environment variable printenv VAR
List environment env or printenv
List Bash exports export -p
One-command variable VAR=value command
Run command with env env VAR=value command
Remove variable for one command env -u VAR command
Keep variable but stop exporting it export -n VAR
Remove variable completely unset VAR
Clean environment env -i command

Shell Variables vs Environment Variables

A shell variable exists in the current Bash process. You create one with the assignment rules covered in Bash variables: name=value with no spaces around =.

In Bash, an exported variable is included in the environment passed to commands and child processes that Bash starts. Use the export builtin to mark a shell variable for export.

Kind Current shell New child process
Shell variable Yes No
Exported variable Yes Yes

export does not create a shared variable between processes. It tells Bash to include the name and value in the environment passed to subsequently started child processes.


Set and Export Environment Variables

You can export in one step or assign first and export the name afterward. Both forms mark a variable for child processes started later in the same shell.

Set a shell variable without exporting it:

bash
MYVAR=hello

MYVAR exists in this shell only until you export it or pass it on a single command line.

Run a child Bash to check whether MYVAR is in its environment. The examples use printf for predictable status lines — see Bash echo vs printf when you choose output commands:

bash
bash -c 'printf "child MYVAR=%s\n" "${MYVAR:-unset}"'
output
child MYVAR=unset

The child reports unset because MYVAR was never exported. Mark the name for export, then run the same check again:

bash
export MYVAR=hello

After export, the same child check should print the stored value:

bash
bash -c 'printf "child MYVAR=%s\n" "$MYVAR"'
output
child MYVAR=hello

You can also assign and export in two steps:

bash
RELEASE_TAG=v2.4.1
export RELEASE_TAG

Either export pattern adds the name to the environment Bash passes to the next child you start.


How Environment Variable Inheritance Works

When Bash runs an external command or starts a child shell, that process receives an environment assembled from exported and inherited variables. The child can read those values, change its own copy, or export new names locally. Changes made in the child do not modify the parent's environment.

text
  ┌────────────────────────┐
  │  Parent shell          │
  │  export MYAPP=prod     │
  │  export PATH           │
  └───────────┬────────────┘
              │ starts child process
              │ exported environment passed
              ▼
  ┌────────────────────────┐
  │  Child process           │
  │  sees MYAPP, PATH (copy) │
  │  cannot modify parent's  │
  │  environment             │
  └────────────────────────┘

If you set LOG_DIR=/var/log but do not export it, subsequently started child processes do not receive LOG_DIR in their environment. The name remains a shell variable in your current Bash session only.

Bash builtins and shell functions normally run in the current shell, so they can read ordinary shell variables without export. External commands and child shells need exported variables unless you pass values specifically for that command.


List Environment Variables

env, printenv, and export -p show related information, but they are used differently.

Command What it shows
env Environment as NAME=value lines; can also run a program with a modified environment
printenv Environment variables only; optional filter by name
export -p Bash builtin listing exported names in declare -x form

Print selected environment variables by name:

bash
printenv HOME PATH SHELL
output
/root
/root/.local/bin:/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
/bin/bash

Values differ by host and session. HOME is your home directory, PATH lists directories Bash searches for commands, and SHELL is the login shell configured for your user.

List the full environment with env or printenv when you need every name. Sorting makes the output easier to scan because variable order is not stable:

bash
env | sort | head
output
COLORTERM=truecolor
HISTCONTROL=ignoredups
HISTSIZE=1000
HOME=/root
HOSTNAME=lab-shell.example
LANG=en_US.UTF-8
LESSOPEN=||/usr/bin/lesspipe.sh %s
LOGNAME=root
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33
MAIL=/var/spool/mail/root

The exact names depend on your session. Child processes receive the environment in this NAME=value form. HISTCONTROL and HISTSIZE show up in many login sessions, but they do not by themselves flush commands to ~/.bash_history—when history looks wrong on disk, troubleshoot in fix Bash history not saving commands.

List variables that Bash has marked for export:

bash
export -p | grep '^declare -x PATH='
output
declare -x PATH="/root/.local/bin:/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"

declare -x shows that PATH has the export attribute.


Pass an Environment Variable to One Command

When you place an assignment before an external command, Bash passes that value to the command only. Any existing value in your current shell remains unchanged. You do not need a separate export for that one invocation.

Run a child with a staging flag for one command:

bash
APP_ENV=staging bash -c 'printf "APP_ENV=%s\n" "$APP_ENV"'
output
APP_ENV=staging

Check that your interactive shell was not changed:

bash
printf "parent APP_ENV=%s\n" "${APP_ENV:-unset}"
output
parent APP_ENV=unset

The parent still has no APP_ENV. The VAR=value command form is the usual pattern for APP_ENV=staging ./deploy.sh, make, or CI wrappers when you need a temporary override.

GNU env runs a command in a modified environment. Assignments on the same line apply only to that command:

bash
env APP_ENV=staging printenv APP_ENV
output
staging

Remove a variable for one command with env -u:

bash
env -u HOME printenv HOME

printenv prints nothing when HOME is not in the environment for that run. Your interactive shell still has HOME set.


Stop Exporting or Unset a Variable

export -n removes the export attribute from a variable without deleting the value in the current shell. unset removes the variable from the current Bash session.

Mark a variable exported, then remove the export attribute:

bash
DEMO=visible
export DEMO
export -n DEMO

Confirm the parent still holds the value:

bash
printf "parent DEMO=%s\n" "$DEMO"
output
parent DEMO=visible

Verify that a new child process no longer receives the variable:

bash
bash -c 'printf "child DEMO=%s\n" "${DEMO:-unset}"'
output
child DEMO=unset

DEMO remains in the parent shell but is no longer included in the environment of new child processes. Use export -n when you want to stop exporting a variable without removing it.

Remove the variable entirely with unset:

bash
export DEMO=visible
unset DEMO

Verify that the variable is no longer set:

bash
printf '%s\n' "${DEMO:-unset}"
output
unset

export -n and unset solve different problems:

Command Value remains in current Bash? Passed to future children?
export -n VAR Yes No
unset VAR No No

PATH Environment Variable

PATH is a colon-separated list of directories Bash searches when you enter a command without a path. After you change PATH, the which command confirms which executable Bash resolves for a given name. In a typical shell session, PATH is also exported so child processes can inherit it. That is why you can type ls instead of /usr/bin/ls.

Inspect the current search path:

bash
printf '%s\n' "$PATH"
output
/root/.local/bin:/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin

Prepend a directory that is not already in PATH for the rest of the session:

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

The new directory should now be the first entry in PATH:

bash
printenv PATH | cut -d: -f1-3
output
/root/tools/bin:/root/.local/bin:/root/bin

Keep the existing $PATH on the right so system directories stay reachable. Avoid replacing the whole path with one directory unless you are deliberately building a minimal environment.


Why Child Processes Cannot Change the Parent

A child receives a copy of the environment when it starts. Changing a variable inside the child updates only that process and any children it starts later. The parent keeps its own value.

Export a name in the parent and let the child overwrite its copy:

bash
export PARENT_VAR=original
bash -c 'PARENT_VAR=changed; printf "inside child PARENT_VAR=%s\n" "$PARENT_VAR"'
output
inside child PARENT_VAR=changed

Read the parent value in the same shell session:

bash
printf "in parent PARENT_VAR=%s\n" "$PARENT_VAR"
output
in parent PARENT_VAR=original

The child printed changed, but the parent still holds original. No script run as a child can export a value back into your interactive shell. A child script can instead print a value for the parent to capture, write it to a file, or load logic in the current shell with the Bash source command instead of spawning a child.


Temporary vs Persistent Environment Variables

Session exports last until you close the shell or unset the name. One-command assignments last only for that command line. Neither writes anything to disk.

To load a variable automatically in future Bash sessions, place it in the startup file read by that type of Bash session. Cron jobs, systemd services, containers, and other process managers may use their own environment configuration. See Bashrc vs bash_profile for which startup file runs on login versus interactive shells.

Their lifetimes are different:

  • export NAME=value — available for the current shell session
  • NAME=value command — available only to that command
  • Startup-file export — loaded again when the relevant Bash startup file runs

Start with a Clean Environment using env -i

env -i starts with an empty environment before applying the assignments you provide on the same command line. Use it when you need to reproduce a bug without inherited session variables.

Run env with only the variables you name:

bash
env -i APP_ENV=test /usr/bin/env
output
APP_ENV=test

Only APP_ENV=test appears. -i ignores the inherited environment and keeps only the variables you explicitly provide.


Common Environment Variable Problems

Symptom Likely cause Fix
Script prints empty variable Assigned in parent but never exported export VAR or use VAR=value ./script.sh
Variable works in terminal, not in cron Cron job does not receive the same environment as your interactive shell Set exports in the job, a wrapper script, or the crontab line
Child changes value, parent unchanged Expected process isolation Return data with stdout, a file, or source in the same shell
command not found after PATH edit Typo or removed system directories Prepend with export PATH="$HOME/tools/bin:$PATH", do not replace the whole path

Avoid storing sensitive values in environment variables on systems where other processes or users may be able to inspect process environments. Child processes inherit exported secrets. Use the secret-management mechanism provided by your application, service manager, container platform, or CI system when appropriate.


Summary

  • VAR=value creates a Bash variable; export VAR makes it available to subsequently started child processes.
  • VAR=value command sets a variable only for that command.
  • env and printenv inspect the environment; export -p shows Bash exports.
  • export -n VAR stops exporting while keeping the Bash variable; unset VAR removes it.
  • Child processes cannot change environment variables in their parent shell.

References


Frequently Asked Questions

Does export make a variable global?

No. export marks a variable to be included in the environment of subsequently executed child processes. It does not make the variable global across unrelated processes, other login sessions, or other users. Already-running children do not pick up changes you make later in the parent shell.

Why can my script not see a variable?

The variable may exist only as a shell variable in the parent terminal and was never exported. Cron jobs, systemd units, and non-interactive scripts may receive a different environment from your interactive shell. Export the name, pass VAR=value ./script.sh, or set the value where that runner reads its environment.

Can a child script export a variable back to its parent?

No. A child receives a copy of the environment at launch. Changes and exports inside the child stay in that process tree. To affect the parent shell, run source script.sh in the same shell (which has other side effects) or return data through stdout, a file, or an explicit API.

env vs printenv vs export -p?

env lists the environment and can run a command with extra, altered, or removed variables. printenv lists or prints environment values without executing a program. export -p shows Bash variables marked with the export attribute, including an exported name that may currently have no value. env and printenv show entries in the process environment.

Finished reading?Beta Get the key takeaways in seconds, then ask follow-up questions
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with 15+ years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)