Chapter 02Lesson 03~45 minutes

Variables, Environment Variables, readonly, and export

Variables are where shell commands become programs. The difficult part is not assignment syntax—it is understanding scope: which values exist only in one Bash process, which are inherited by child processes, and which should never be exported.

BeginnerBash scriptingHands-on lab

Learning objectives

By the end of this lesson

  • Create and expand Bash variables without accidental word splitting.
  • Distinguish shell variables from environment variables.
  • Explain how export affects child-process inheritance.
  • Use readonly and unset deliberately.
  • Inspect environment state without exposing secrets unnecessarily.

1. Assignment syntax is whitespace-sensitive

Bash assignment syntax places no spaces around =. The text on the left is a variable name; the text on the right is a shell word that undergoes specific expansions before assignment.

project="devops-academy"
chapter=2
empty=""

printf 'project=%s\n' "$project"
printf 'chapter=%s\n' "$chapter"
printf 'empty=<%s>\n' "$empty"

# This is NOT assignment:
# project = "devops-academy"
# Bash would try to run a command named "project".
Quoting habit

Quote variable expansions by default: "$variable". Unquoted expansion can trigger word splitting and pathname expansion in contexts where you do not intend it.

2. Name variables for their role, not their temporary value

Portable shell variable names use letters, digits, and underscores and do not begin with a digit. Bash is case-sensitive, so path, Path, and PATH are different.

repository_root="/srv/app"
artifact_dir="$repository_root/build"
retry_limit=5

printf '%s\n' "$artifact_dir" "$retry_limit" 

A common convention is lowercase names for script-local data and uppercase names for environment variables or intentional constants. This is a convention rather than a Bash requirement, but it helps avoid accidental collision with variables such as PATH, HOME, IFS, and SHELL.

3. Shell variables and environment variables are different layers

Execution model
flowchart TB
  P["Parent Bash process"] --> V["Shell variable: project=academy"]
  P --> E["Exported variable: REGION=eu"]
  P --> C["Child process"]
  E -. inherited .-> C
  V -. not inherited by default .-> C
project="academy"
export REGION="eu-central"

bash -c '
  printf "project=%s\n" "${project-unset}"
  printf "REGION=%s\n" "${REGION-unset}"
'

The child Bash receives REGION because it was exported. It does not receive the unexported shell variable project.

4. export marks a name for child-process inheritance

You can assign and export in one command or export a previously assigned variable:

API_MODE="staging"
export API_MODE

export LOG_LEVEL="info"

env | grep -E '^(API_MODE|LOG_LEVEL)='

export does not create global mutable storage shared by all processes. A child receives a copy of the environment when it starts. If the child changes its own copy, that does not modify the parent's variable.

export COLOR=blue
bash -c 'COLOR=green; printf "child=%s\n" "$COLOR"'
printf 'parent=%s\n' "$COLOR" 

5. Environment variables are convenient, not secret storage

Environment variables are widely used for configuration because processes can read them easily. That convenience does not make them a secure secret vault. Depending on the operating system, runtime, crash reporting, diagnostic tooling, process privileges, and CI configuration, environment values may be observable.

Prefer purpose-built secret mechanisms when available: CI secret stores, container orchestrator secret facilities, Vault-like systems, protected files with strict permissions, or short-lived credentials.

Security habit

Do not print the full environment in production logs. Commands such as env or set can expose tokens, passwords, keys, and connection strings.

6. readonly protects a shell variable from reassignment

readonly is useful for values that should not change after initialization.

readonly SCRIPT_NAME="artifact-check"
readonly MAX_RETRIES=3

printf '%s retries=%s\n' "$SCRIPT_NAME" "$MAX_RETRIES"

# This would fail:
# MAX_RETRIES=10

Readonly is a guardrail inside the current shell, not a cryptographic protection mechanism. A new process can define its own value with the same name.

7. Unset, empty, and absent are three states worth distinguishing

An unset variable does not exist in the current shell variable set. An empty variable exists but contains a zero-length string. Some parameter-expansion operators distinguish these states.

unset missing
empty=""

printf 'missing: <%s>\n' "${missing-unset}"
printf 'empty:   <%s>\n' "${empty-unset}"

declare -p empty
declare -p missing 2>/dev/null || printf 'missing is not declared\n'

This distinction matters for configuration. “Not provided” may mean use a default, while “provided but empty” may mean intentionally disable a feature.

8. Know the high-impact predefined variables

VariablePurposeWhy it matters
PATHDirectories searched for commandsChanging it can alter which executable runs
HOMEUser home directoryCommon base for user configuration and caches
PWDLogical current directoryUseful but can change during script execution
OLDPWDPrevious directoryMostly interactive convenience
IFSInternal field separatorAffects splitting; careless changes can corrupt parsing
BASH_VERSIONRunning Bash versionUseful for feature checks and diagnostics
Defensive practice

Avoid reusing special shell names for unrelated data. A variable named PATH is not a harmless generic path variable.

9. Hands-on lab: observe environment inheritance

Build a parent/child demonstration and verify exactly what crosses the process boundary.

mkdir -p "$HOME/devops-academy/bash/chapter02/lesson03"
cd "$HOME/devops-academy/bash/chapter02/lesson03"

cat > child.sh <<'EOF'
#!/usr/bin/env bash
printf 'LOCAL_ONLY=%s\n' "${LOCAL_ONLY-unset}"
printf 'DEPLOY_ENV=%s\n' "${DEPLOY_ENV-unset}"
DEPLOY_ENV=changed-in-child
printf 'child changed DEPLOY_ENV=%s\n' "$DEPLOY_ENV"
EOF

LOCAL_ONLY="parent-shell"
export DEPLOY_ENV="staging"

bash child.sh
printf 'parent still DEPLOY_ENV=%s\n' "$DEPLOY_ENV" 

Verification checklist

10. Knowledge check

Question 1. What does export NAME change?

Question 2. Can a child process change the parent's exported variable by assigning a new value?

Question 3. Why should env output be treated carefully in CI logs?

11. Summary

Bash variables live in a shell process. Exported variables are copied into child-process environments. That distinction explains many automation bugs: a value exists in your terminal but disappears in a script, or a child changes a setting but the parent remains unchanged. Treat environment configuration as a process interface and treat environment secrets as sensitive data.

12. Further reading

  • GNU Bash Reference Manual — Shell Variables and Bourne Shell Builtins.
  • POSIX environment-variable definitions.
  • Linux environ(7) manual page.
  • OWASP guidance on secrets management and CI/CD credential exposure.
Next lesson

Parameter Expansion and Default Values

Continue Chapter 2 by building on this execution and data model.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.