An environment is a collection of name-value strings supplied to a program when it starts. A child normally receives the environment selected by its parent; changing that child’s copy does not rewrite the parent’s or a sibling’s copy. This is a useful configuration boundary, but it is not a promise that the command has no side effects. The program can still modify shared files, databases, or other resources using those settings.
Export selects values for another program
A shell variable and an environment entry are related but distinct. Assigning DEMO_SETTING=local creates or updates a shell parameter. export DEMO_SETTING marks it for inclusion when the shell launches external commands. A new Bash process normally cannot see an unexported shell variable, even though the parent can print it. Merely placing an assignment in a configuration file does not load that file into every shell.
A value inherited through the environment is already marked for export by Bash. Changing its value retains that attribute, so a later grandchild can inherit the changed value without another export statement. unset removes the variable; Bash’s export -n removes its export attribute while retaining the shell value. The relevant question is which environment the immediate launcher supplied, not whether every ancestor repeated an export command.
A subshell is not a freshly executed shell
Parenthesized command groups and command substitution run in a subshell environment copied from the current shell. They can see ordinary shell variables that were never exported. Their assignments still do not flow back into the outer shell. This differs from executing a separate shell program, which receives exported entries rather than a complete copy of the parent’s shell parameter table.
Sourcing a file with . runs its commands in the current shell. Its assignments can therefore change the caller directly; it is not an exception to process isolation because no separate process is returning those changes. A sourced file is executable shell code, not passive configuration. Only source a file whose contents you intend to execute in that context.
Per-command settings have a defined lifetime
A prefix such as DEMO_SETTING=once external-command supplies a value for that invocation without permanently changing the surrounding shell’s value. This statement concerns an external command; assignment behavior around special builtins and functions has additional shell-specific rules. Do not generalize it into a claim that every command-looking construct creates an isolated process.
At the operating system interface, execve takes an explicit environment array and replaces the current process image. It does not itself create a new process. A launcher can preserve, filter, or replace the environment, which explains why privilege boundaries, service managers, and container configuration may produce values different from an interactive terminal. Process ancestry alone is insufficient to establish the actual settings delivered to an application.
When it breaks
Editing a terminal’s environment does not update an already running service. Likewise, changing an environment file has no effect until the relevant launcher rereads it and starts the process, unless the application provides its own reload mechanism. Check the configuration path used by that service and verify the resulting behavior in the process that actually handles requests.
Treat missing and empty values separately, because applications may assign them different meanings. Environment strings cannot contain an embedded NUL, and platforms limit the combined argument and environment size, so they are unsuitable for arbitrary binary payloads or unbounded documents. Inspect only the specific nonsecret keys needed for diagnosis; dumping an entire environment can expose credentials inherited by unrelated tools.
Check it quickly
Run this Bash example. It prints only a dedicated demonstration variable, uses no external configuration file, and leaves the surrounding terminal unchanged. The small here-document passed to . contains the complete trusted configuration used for the sourcing test.
BASH_ENV= bash --noprofile --norc <<'BASH'
unset DEMO_SETTING
DEMO_SETTING=local
printf 'shell=%s\n' "$DEMO_SETTING"
bash --noprofile --norc -c 'printf "unexported-child=%s\n" "${DEMO_SETTING-unset}"'
(printf 'subshell=%s\n' "$DEMO_SETTING")
export DEMO_SETTING
bash --noprofile --norc <<'CHILD'
printf 'exported-child=%s\n' "$DEMO_SETTING"
DEMO_SETTING=child
bash --noprofile --norc -c 'printf "grandchild=%s\n" "$DEMO_SETTING"'
CHILD
printf 'parent-after-child=%s\n' "$DEMO_SETTING"
DEMO_SETTING=once bash --noprofile --norc -c 'printf "one-command=%s\n" "$DEMO_SETTING"'
printf 'parent-after-command=%s\n' "$DEMO_SETTING"
. /dev/stdin <<'CONFIG'
DEMO_SETTING=sourced
CONFIG
printf 'parent-after-source=%s\n' "$DEMO_SETTING"
BASH
The unexported child prints unset, while the parenthesized subshell sees local. After export, the new child sees local, changes its own copy, and passes child to its grandchild. The outer shell remains local. The one-command override prints once, and the following parent check is still local.
Finally, sourcing changes that outer child shell’s value to sourced. Together these observations distinguish shell parameters, exported entries, subshell copies, per-command overrides, and execution in the current shell. They establish variable scope, not the absence of application side effects or the correctness of a service’s deployment configuration.
Sources
- Bash execution environment, exported entries versus the state copied into subshells, checked 2026-09-07.
- Bash environment, export attributes and per-command assignments, checked 2026-09-07.
- POSIX exec functions, process-image replacement and explicit environment arrays, checked 2026-09-07.