opscape

Shell builtins · bash builtin

export

Mark shell variables (or bash functions) to be passed to every program the shell starts.

export [-fn] [NAME[=VALUE] ...]    or    export -p

9 of 9 examples run in Debian 13.7 on 2026-10-31 · manual: bash(1), SHELL BUILTIN COMMANDS

A shell variable exists only inside the shell that set it. export NAME puts it into the environment, the list of NAME=value strings that is copied into every child process: programs, scripts and subshells started from then on. That is how PATH, HOME, EDITOR or an API_TOKEN reach the programs that read them.

The copy goes one way, downward. A child can change its copy, but nothing it does reaches the parent, which is why a script cannot set a variable in your terminal (unless you source it). And export affects children started after it; a program already running keeps the environment it was born with.

To give a variable to one command only, skip export and prefix the command: LANG=C sort file. To remove a variable from the environment but keep it in the shell, use export -n; to remove it entirely, unset. export -p (or plain export) lists what is exported.

Examples

Make a variable visible to child processes

COLOR=blue
bash -c 'echo "child sees COLOR=[$COLOR]"'
export COLOR
bash -c 'echo "child sees COLOR=[$COLOR]"'
output
child sees COLOR=[]
child sees COLOR=[blue]

Before export, COLOR is a plain shell variable and the child bash gets nothing. After it, the variable is part of the environment of every command started later.

Set and export in one step

export API_URL=http://localhost:8080 RETRIES=3
env | grep -E '^(API_URL|RETRIES)='
output
API_URL=http://localhost:8080
RETRIES=3

export NAME=value assigns and exports at once, for as many names as you list. env, being a separate program, shows what children receive.

Set a variable for a single command

TZ=Asia/Tokyo date +%H:%M-%Z
date +%H:%M-%Z
echo "TZ in this shell: [${TZ-unset}]"
output
05:19-JST
20:19-UTC
TZ in this shell: [UTC]

A NAME=value prefix puts the variable in that one command's environment only. date reads TZ from its environment; afterward the shell still has its own value (UTC in this sandbox), untouched.

A child cannot change the parent's variable

export STAGE=dev
bash -c 'STAGE=prod; echo "child changed it to $STAGE"'
echo "parent still has $STAGE"
output
child changed it to prod
parent still has dev

The child receives a copy of the environment. Its assignment changes only that copy, so the parent's value is untouched. Pass results back through output instead: STAGE=$(cmd).

List exported variables

export -p | grep -E 'HOME|EDITOR|LANG'
explain
output
declare -x EDITOR="vi"
declare -x HOME="/home/dev"
declare -x LANG="C.UTF-8"

export -p prints each exported variable as a declare -x command that could recreate it. Variables the shell inherited, such as HOME and LANG, are exported automatically; EDITOR comes from the environment this sandbox starts with.

Stop exporting a variable with -n

export TOKEN=abc123
export -n TOKEN
echo "shell still has: $TOKEN"
bash -c 'echo "child sees: [$TOKEN]"'
output
shell still has: abc123
child sees: []

export -n removes the export flag but keeps the value in the current shell. Useful for keeping a secret away from the commands you run next.

Add a directory to PATH

mkdir -p ~/bin
printf '#!/bin/sh\necho "hello from ~/bin"\n' > ~/bin/hello
chmod +x ~/bin/hello
export PATH="$HOME/bin:$PATH"
hello
echo "$PATH" | tr : '\n' | head -n 3
output
hello from ~/bin
/home/dev/bin
/usr/local/bin
/usr/bin

PATH is already exported, so reassigning it is enough, but writing export makes the intent clear. Prepending gives your directory priority. In a startup file this line belongs in ~/.profile or ~/.bashrc.

Export a function to child bash processes with -f

greet() { echo "hello, $1"; }
export -f greet
bash -c 'greet child'
printf '%s\n' a b | xargs -n 1 bash -c 'greet "$1"' _
output
hello, child
hello, a
hello, b

export -f passes a function through the environment to child bash processes. This is how to call a shell function from xargs, find -exec or parallel, which can only start programs. Each value is passed as $1 (after a placeholder $0) rather than pasted into the code, so odd file names cannot inject commands. Other shells such as dash will not see exported functions.

Subshells see unexported variables, programs do not

LOCAL_ONLY=yes
( echo "subshell: $LOCAL_ONLY" )
echo "pipe: $(echo "$LOCAL_ONLY")"
bash -c 'echo "new bash: [$LOCAL_ONLY]"'
output
subshell: yes
pipe: yes
new bash: []

( ... ) and $( ... ) are forks of the current shell, so they inherit every variable. bash -c is a new program and gets only the environment. This difference is why a variable "works in the script but not in the program it runs".

Common pitfalls

  • Expecting a script to export a variable into your terminal. Children cannot change their parent; source the script instead.

  • export VAR=$(cmd) returns the status of export, which is 0, so a failing cmd goes unnoticed by set -e or &&. Assign first, then export VAR.

  • Spaces around =: export NAME = value exports three names. Write NAME=value with no spaces.

  • Exporting secrets more widely than needed: every program you start afterward can read them, including from /proc/PID/environ. Prefer a per-command prefix.

GNU vs BSD and macOS

The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).

  • export NAME=value and export -p are POSIX. export -n and export -f are bash-only; in dash they are errors. zsh supports export with the same basic syntax.

Options worth knowing

A selection, described in our own words. For the full list, see bash(1), SHELL BUILTIN COMMANDS.

OptionWhat it does
-pList all exported variables as reusable declare -x commands.
-nRemove the export flag from the named variables, keeping their values.
-fTreat the names as shell functions and export those (bash only).

Related