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.
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".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
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.
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=10Readonly 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
PATHDirectories searched for commandsChanging it can alter which executable runsHOMEUser home directoryCommon base for user configuration and cachesPWDLogical current directoryUseful but can change during script executionOLDPWDPrevious directoryMostly interactive convenienceIFSInternal field separatorAffects splitting; careless changes can corrupt parsingBASH_VERSIONRunning Bash versionUseful for feature checks and diagnosticsAvoid 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.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this address.