Skip to content
VLSI Mentor

Linux & Bash · Part I · Module 10 · The Environment, PATH and Shell Startup

The Environment: Variables a Program Inherits

Every program you run starts with a list of name-and-value pairs handed to it by whatever started it. That list is the environment, and almost every question of the form 'why does this setting work here but not there' is a question about which process got a copy of what.

Intermediate18 min readLinuxPart IFundamentalsEnvironmentBash

Part I · Module 10 · Chapter 10.1 · The Environment: Variables a Program Inherits

Chapter 8.1 said a plain assignment is local to one shell and export puts a value where children can see it. Chapter 7.4 said a child gets a copy and nothing comes back. This module is where those two facts become the subject rather than an aside — because between them they explain why a setting that plainly works in your terminal has no effect in a script, and vice versa.

1. Two Kinds of Variable

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ LOCAL=a
$ export SHARED=b

$ bash -c 'echo "child: LOCAL=[$LOCAL] SHARED=[$SHARED]"'
child: LOCAL=[] SHARED=[b]

Both variables exist in your shell. Only one of them reached the child.

  • A shell variable is a name and value the shell remembers. It is yours alone.
  • An environment variable is a shell variable that has additionally been marked for export, which places it in the list handed to every program you start.

The two commands that show you each list:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ env | grep -E '^(LOCAL|SHARED)='
SHARED=b

$ set | grep -E '^(LOCAL|SHARED)='
LOCAL=a
SHARED=b

env prints the environment — the exported ones only. set prints every shell variable, exported or not (along with functions, which is why it is usually piped somewhere).

What a child process does and does not receive. A plain assignment makes a shell variable, which lives in one shell and is never handed down, so a child sees nothing. An exported variable is placed in the environment, and the child receives a copy of it — but only a copy, so anything the child changes is lost when it exits.A plain assignmentthe value lives in this shellonlyNot in the environmentnothing is handed downThe child sees nothingthe variable is empty thereAn exported assignmentthe value is placed in theenvironmentThe child receives acopya copy, not the originalChanges never come backthe copy dies with the child12
Figure 1 — what a child process does and does not receive. A plain assignment makes a shell variable that lives in one shell and is never handed down, so a child sees nothing at all. An exported variable is placed in the environment and the child receives a copy of it — but a copy only, which is why anything the child changes is lost the moment it exits.

2. Exporting

Three spellings, all equivalent:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
export TOOL_HOME=/opt/tool          # assign and export together
TOOL_HOME=/opt/tool; export TOOL_HOME   # assign, then export
declare -x TOOL_HOME=/opt/tool      # the same thing, spelled differently

Export is a property of the variable, not of the value. Once a variable is exported it stays exported, and later assignments to it are automatically visible to children:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ V=1
$ bash -c 'echo "pre-export child=[$V]"'
pre-export child=[]

$ export V
$ bash -c 'echo "post-export child=[$V]"'
post-export child=[1]

Note that export V on its own — with no value — promoted a variable that already existed. That is a legitimate and occasionally useful form.

The reverse exists too:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ export -n V
$ bash -c 'echo "after -n child=[$V]"'
after -n child=[]

$ echo "parent still V=[$V]"
parent still V=[1]

export -n removes the export mark without unsetting the variable. The value is still yours; it simply stops being handed down. (unset V removes it entirely.)

To see what is currently exported, with quoting that can be pasted back:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ export -p | grep ' T='
declare -x T="/opt/tool"

3. Why Nothing Comes Back

This is the consequence that matters most, and it is worth proving rather than asserting:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ export V=parent

$ bash -c 'export V=child'

$ echo "after the child exported: V=[$V]"
after the child exported: V=[parent]

The child did everything right. It exported. It succeeded. And your shell is unchanged, because the child was working on its copy.

4. Changing It for One Command

Because the environment is copied at the moment a program starts, you can change what gets copied without changing your own:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ export V=orig

$ env V=temp bash -c 'echo "inner=[$V]"'
inner=[temp]

$ echo "outer=[$V]"
outer=[orig]

env NAME=value command runs that command with an altered environment and leaves yours alone. The shell has its own shorter spelling of the same idea, the assignment-prefix form from Chapter 8.1:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ V=temp bash -c 'echo "inner=[$V]"'
inner=[temp]

Both are the right tool for "run this one thing differently" — a different tool version, a debug flag, a language setting:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
LC_ALL=C sort data.txt            # byte order, not the locale's collation
TOOL_HOME=/opt/tool-2.1 ./run.sh  # this run only
DEBUG=1 make                      # one noisy build

5. The Variables Worth Knowing

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ env | sort | head -12

Most of what you see is noise from your desktop session. About a dozen matter.

VariableHolds
PATHWhere to look for programs — Chapter 10.2
HOMEYour home directory; what ~ expands to
USERYour login name
SHELLYour login shell, not necessarily the one running
PWDThe current directory, maintained by the shell
OLDPWDThe previous one — what cd - uses
TERMThe terminal type, which full-screen programs need
LANG / LC_*Language and locale — affects sorting and messages
EDITOR / VISUALWhich editor other programs should open
TMPDIRWhere temporary files should go
HOSTNAMEThe machine's name

Where engineering settings live

In real work the environment is how tools are configured, and the names are conventional rather than standard:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
export VCS_HOME=/opt/synopsys/vcs/2024.09
export LM_LICENSE_FILE=27000@licsrv1:27000@licsrv2
export PATH="$VCS_HOME/bin:$PATH"
export UVM_HOME="$VCS_HOME/etc/uvm"

Note the third line building on the first, and the colon-separated list in the second — LM_LICENSE_FILE uses the same colon convention as PATH, and for the same reason: an ordered list of places to try. Module 25 covers EDA environments properly.

6. The Environment Is Only Strings

There are no types, no numbers, no lists, no nesting. Every value is text, exactly as Chapter 8.1 said of shell variables.

Two consequences:

A "list" is a string with a separator by convention. PATH uses colons; so does LM_LICENSE_FILE. Nothing enforces it — the programs that read them agree to split on colons, and that agreement is the whole mechanism.

A Bash array cannot be exported.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ arr=(one two three)
$ export arr
$ bash -c 'echo "child sees: [$arr]"'
child sees: [one]

The child received only the first element, because the environment has no way to represent an array — the child's shell saw a plain string. Arrays are Chapter 19.2; the environment-level fact is that they stop at the process boundary.

7. Diagnosing the Usual Problem

"It works in my terminal but not in the script" is almost always one of four things, and you can tell them apart in a minute.

Is it exported at all?

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ echo "$TOOL_HOME"
/opt/tool
$ env | grep TOOL_HOME

A value you can see but env cannot means it is a shell variable, not an environment one. Add export.

Is it exported in the right place? A setting typed into one terminal is in that terminal only. A new terminal, a scheduled job and a remote connection each get a fresh shell — which is Chapter 10.3.

Is something later overwriting it? Check what the program actually received rather than what you think you set:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
env | sort > /tmp/before.txt
./setup.sh                      # or whatever you suspect
env | sort > /tmp/after.txt
diff /tmp/before.txt /tmp/after.txt

Is the environment being scrubbed? sudo, cron, systemd and most continuous-integration runners deliberately start with a reduced environment. sudo in particular resets PATH by default — so a program you can run is one sudo cannot find. sudo -E preserves the environment where policy allows it.

Practice

1 — See both lists.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
LOCAL=a; export SHARED=b
env | grep -E '^(LOCAL|SHARED)='
set | grep -E '^(LOCAL|SHARED)='
bash -c 'echo "child: LOCAL=[$LOCAL] SHARED=[$SHARED]"'

2 — Promote and demote.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
V=1
bash -c 'echo "before: [$V]"'
export V;    bash -c 'echo "after export: [$V]"'
export -n V; bash -c 'echo "after -n: [$V]"'
echo "parent still has [$V]"

3 — Prove nothing comes back.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
export V=parent
bash -c 'export V=child; echo "  child set it to $V"'
echo "parent: $V"

4 — Change one command's environment.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
export V=orig
V=temp bash -c 'echo "inner=[$V]"'
env V=temp bash -c 'echo "inner=[$V]"'
echo "outer=[$V]"

5 — Strip it bare.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
env -i bash -c 'echo "HOME=[$HOME]"; echo "PATH=[$PATH]"; ls /tmp >/dev/null && echo "ls still found"'

6 — Catch SHELL lying.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
echo "SHELL says: $SHELL"
echo "actually running: $(ps -p $$ -o comm=)"

7 — Watch an array fail to cross.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
arr=(one two three)
export arr
bash -c 'echo "child sees: [$arr]"'

8 — Diff an environment.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
env | sort > /tmp/before.txt
export NEWTHING=1
env | sort > /tmp/after.txt
diff /tmp/before.txt /tmp/after.txt

9 — Look at your own, if you are on Linux.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
cat /proc/$$/environ | tr '\0' '\n' | sort | head

Check Your Understanding

1. TOOL=/opt/tool then ./run.sh, and the script reports TOOL as empty. What is missing?

2. What is the difference between env and set?

3. A script does export RESULTS=/tmp/out as its last line. After running it, echo $RESULTS prints nothing. Why does export not help?

4. What does export -n V do that unset V does not?

5. You run env -i ./tool and it fails with "command not found" for something that works normally. What happened?

6. Why should a password never live in an environment variable on a shared machine?

8. Remember

  • A variable can also be set, exported and wrong — Chapter 33.4 measures one pointing at a real project area that was not the intended one, reporting TEST PASSED for a design that was never tested.
  • A shell variable lives in one shell; an exported one is placed in the environment.
  • The environment is a list of strings copied into a program when it starts. Copied, not shared.
  • env shows the exported ones; set shows all of them.
  • export is a property of the variable, so later assignments are exported automatically.
  • export V promotes an existing variable; export -n V demotes it without unsetting it.
  • No child can change its parent's environment. There is no mechanism, and that is a safety property as much as a limitation.
  • VAR=value command and env VAR=value command change one command's environment and leave yours alone.
  • env -i empties it, though Bash still supplies a minimal default PATH — which is how build systems and cron fail.
  • $SHELL is your login shell, not the one running. Use ps -p $$ -o comm=.
  • Everything is a string. A "list" is a colon-separated string by convention, and a Bash array cannot cross the boundary.
  • The environment is readable by other processes. Never put secrets in it.
  • "Works in my terminal, not in the script" is: not exported, exported in the wrong place, overwritten later, or deliberately scrubbed. When a script reads a setting from the environment, Chapter 21.3 states the precedence that decides which source wins.
  • A licence variable is a colon-separated search list, not a path — Chapter 25.4 takes it apart, including the four distinct ways it fails.

The most important environment variable of all decides which program runs when you type a name. The next chapter is PATH — how it is searched, how to prove which copy of a command you are getting, and how to add to it without destroying what was already there.

Where this fits

Part of the Linux & Bash curriculum.