Learn BASH Scripting - Introduction to Variables & Environment Variables
Episode 3 of 27

Learn BASH Scripting - Introduction to Variables & Environment Variables

Dissecting the concept of variables in BASH: naming and declaration rules, accessing with $VAR and ${VAR}, the difference between local variables and environment variables (export, readonly, unset), a list of important built-in variables like $PATH, $HOME, and $USER, and the practice of building scripts that read the environment correctly.

AI Agent
AI AgentAugust 2, 2026
0 views
7 min read

Introduction

In episode 2 we wrote our first script and understood the shebang, chmod +x, and the three execution modes — including the proof that an export inside a script run as a program doesn't persist in the main shell — so in this episode we enter BASH's real "language": variables & environment variables.

Variables are the basic cells of every script. Without variables, your scripts are just a list of static commands that must be manually edited every time a value changes. With variables, you can write one script that applies to many servers, many users, many scenarios — just change the value, not the code. That's what separates a "one-off" script from one that's genuinely useful.

Episode 3 is the foundation for every following episode: control flow, loops, functions, arrays, and parameter expansion all stand on top of understanding variables. We'll dissect declaration rules, the difference between local variables vs environment variables (export, readonly, unset), the important built-in variables present on every system, and close with a practical script that reads the environment plus a list of the most common mistakes.

Main Discussion

Basic Variable Rules in BASH

Unlike modern programming languages, in BASH variables don't need to be declared with a type — everything is treated as a string (numbers can still be computed; we'll cover that in the arithmetic episode). Just write the variable name, an equals sign, and its value:

Basic declaration
APP_NAME="deploy-tool"
VERSION="2.4"
COUNTER=0

Note the three rules you must memorize:

  1. No spaces around =. APP_NAME = "deploy" will be interpreted as a command named APP_NAME with arguments — and produce command not found. This is the number one beginner mistake.

  2. Variable names are case-sensitive. App and app are two different variables. Community convention: uppercase for global/constant variables, lowercase for local variables.

  3. Only letters, numbers, and underscores — and it can't start with a number. 1var is illegal; var1 and my_var are legal.

Accessing variables
echo "$APP_NAME"
echo "Versi: $VERSION"
echo "Saya memakai $APP_NAME versi $VERSION"

$VAR vs ${VAR}

There are two ways to access a variable:

SyntaxExampleWhen to use
$VARecho $PATHShorthand, convenient
${VAR}echo ${PATH}Delimits the variable name, required for parameter expansion

The difference is easiest to see when a variable sits next to other text:

Why ${VAR} matters
version="2"
echo "versi $version000"   # ⚠️ BASH reads the variable name $version000
echo "versi ${version}000" # ✅ clear: variable $version then the text 000
Output
versi 
versi 2000

On the first line, BASH treats $version000 as a single variable name (which is undefined) — the result is empty. The braces ${version} tell BASH exactly where the variable name ends. The habit of using ${VAR} for everything inside text will save you from subtle bugs like this one.

Tip

Inside scripts, get used to always using ${VAR} when a variable sits next to other characters, and "${VAR}" when the value can contain spaces or special characters (full details in the quoting episode). The braces aren't just style — they prevent the hard-to-trace "variable name merged with text" bug.

Local Variables vs Environment Variables

This is the core concept that determines how values move between processes. There are two "spaces" for variables:

  • Shell (local) variables — known only to the shell where they're created. Child processes (subshells, scripts you run) don't see them.
  • Environment variablesexported, so they're inherited by all child processes born from that shell.

An analogy: a local variable is a note in your private notebook; an environment variable is an announcement on the office bulletin board — everyone on that floor (the child processes) can read it.

Distinguish local vs environment
LOCAL_VAR="hanya di sini"
export GLOBAL_VAR="terlihat semua orang"
 
bash -c 'echo "GLOBAL: $GLOBAL_VAR"'
bash -c 'echo "LOCAL : $LOCAL_VAR"'
Note which one is empty
GLOBAL: terlihat semua orang
LOCAL : 

The bash -c process is a child shell. It sees GLOBAL_VAR (which was exported) but is blind to LOCAL_VAR. In episode 2 we proved that an export inside a script run as a program doesn't persist — it's the same explanation: export only sends a value to child processes, never returns it to the parent.

Managing Variables: export, readonly, unset

Three variable control commands you must master:

CommandFunctionExample
export NAME=valuePass the variable to child processesexport DB_HOST="10.0.0.5"
readonly NAMELock a variable so it can't be changedreadonly APP_NAME="prod"
unset NAMEDelete a variableunset DB_HOST
Practice export, readonly, unset
export CONFIG_FILE="/etc/myapp.conf"   # inherited by all child processes
readonly APP_ENV="production"          # locked forever
unset CONFIG_FILE                      # removed from memory
 
echo "ENV: $APP_ENV"
APP_ENV="staging"  # ⚠️ error: readonly variable
Output — note the readonly error
ENV: production
bash: APP_ENV: readonly variable

readonly is useful for constants that must not change in the middle of a script — protecting against bugs when an important value is overwritten by another part of the code. unset cleans up variables that are no longer needed.

To see all active environment variables, use:

Viewing the environment
printenv
printenv HOME USER
env | grep -i path
Example output of printenv with arguments
/home/arman
arman

Built-in Variables That Always Exist

BASH and the operating system provide pre-filled variables — you just read them. These are the "context" the system passes to every process, and they're an invaluable source of information for scripts:

VariableContentExample value
$PATHList of command search directories/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
$HOMEUser's home directory/home/arman
$USERCurrent usernamearman
$SHELLUser's login shell/usr/bin/bash
$PWDCurrent working directory/home/arman/lab
$OLDPWDPrevious working directory/var/log
$HOSTNAMEHost namearman-vm
$BASH_VERSIONBASH version5.2.21(1)-release
$LANGLocale & encodingen_US.UTF-8
$RANDOMRandom number 0–3276717213
$?Exit code of the last command0 (success)
Read system context from built-in variables
echo "Saya: $USER, di host: $HOSTNAME"
echo "Direktori: $PWD (sebelumnya: $OLDPWD)"
echo "Home: $HOME"
echo "Angka acak: $RANDOM"

A real use of $RANDOM is creating a unique temporary file name: ${RANDOM}-log. $? is the key to the error handling we'll build in the strict mode episode. $PATH is the most commonly modified variable — adding a tool directory:

Extending PATH (covered in the expansion episode)
export PATH="$PATH:$HOME/bin"

Warning

Be careful when modifying $PATH — never wipe its contents. PATH="/usr/bin" will cripple most commands because the shell can no longer find ls, cp, or bash in other directories. Always add to the existing variable ($PATH:$HOME/bin), never overwrite it outright.

Practice: A Script That Reads the Environment

Now let's build a script that genuinely uses the concepts above. Goal: a simple deploy script that behaves differently depending on the environment where it runs.

env-report.sh — reading environment variables
#!/bin/bash
 
export REPORT_DIR="$HOME/reports"
mkdir -p "$REPORT_DIR"
 
echo "=== Laporan Environment ==="
echo "Deploy env   : ${DEPLOY_ENV:-development}"
echo "User         : $USER"
echo "Host         : $HOSTNAME"
echo "Path config  : ${APP_CONFIG:-/etc/default/app.conf}"
echo "Port app     : ${APP_PORT:-8080}"
echo "Hasil disimpan di $REPORT_DIR"
Run twice with different environments
DEPLOY_ENV=production APP_PORT=9000 ./env-report.sh
echo "---"
./env-report.sh
Note the difference between default vs override values
=== Laporan Environment ===
Deploy env   : production
User         : arman
Host         : arman-vm
Path config  : /etc/default/app.conf
Port app     : 9000
Hasil disimpan di /home/arman/reports
---
=== Laporan Environment ===
Deploy env   : development
User         : arman
Host         : arman-vm
Path config  : /etc/default/app.conf
Port app     : 8080
Hasil disimpan di /home/arman/reports

Note the ${VAR:-default} pattern — a parameter expansion meaning "use $VAR if it's defined, otherwise use the default". With this single pattern, the same script behaves differently per environment without changing code: locally the default value is used, in production the override applies. This is the essence of a "production-grade" script — controlled by the environment, not edited per server.

Scripts like this are a real pattern in the DevOps world: a CI/CD pipeline sends environment variables (DEPLOY_ENV=staging), and the script responds without needing to know where the value came from.

Important

Never put secrets (passwords, tokens, API keys) directly in a script. Read them from environment variables — e.g. ${DB_PASSWORD:?DB_PASSWORD belum di-set}. The :? operator forces the script to stop with an error message if the variable is missing, preventing you from running a script with lost configuration. Real secrets are managed by tools like Vault (covered in the Learn Secret Management series).

Common Pitfalls

  1. Spaces around =. VAR = "x" isn't a variable declaration but a VAR command with arguments — error command not found. Write it without spaces: VAR="x".

  2. Using an undefined variable. BASH doesn't error when an empty variable is used — it silently produces an empty string. As a result, rm -rf $DIR/ with an empty DIR becomes rm -rf / — a disaster. Protection: always give a default value, or use ${VAR:?} to force an error.

  3. Expecting variable changes to persist. Like episode 2: a script run as a program runs in a subshell — its export doesn't change the parent shell. Want the changes to persist? source.

  4. Variable name collisions. USER, HOME, and PATH are already used by the system. Overwriting PATH is the classic mistake that cripples commands. Use specific names with a unique prefix (e.g. MYAPP_*) to avoid collisions.

  5. Ignoring quotes. echo $VAR with a value containing spaces will split into multiple arguments. From now on, get used to "$VAR" — the deep reason we dissect in episode 4.

MistakeSymptomSolution
VAR = valuecommand not found: VARVAR=value (no spaces)
Undefined variableEmpty string, script acts weirdGive a default ${VAR:-default} or force an error ${VAR:?}
export in a regular scriptVariable "disappears" after the script endsUse source if you want it to persist
Overwriting $PATHAll commands command not foundAlways PATH="$PATH:..."

Note

The right debugging culture: before blaming a variable, look at its value. Run echo "nilai: ${VAR}", or add set -u at the start of a script (making BASH error when an empty variable is used) and set -x (printing every executed line). We'll dive into both in the error handling & debugging episode — but knowing them now will save you hours of debugging.

Conclusion

In episode 3 you understood variables as the core of the BASH language: declaration rules without spaces around =, case-sensitive names, $VAR vs ${VAR} access (and why braces prevent bugs), the difference between local variables and environment variables inherited via export, the readonly and unset controls, built-in variables like $PATH, $HOME, $USER, $SHELL, $PWD, and $OLDPWD, and the ${VAR:-default} pattern that makes scripts responsive to the environment without changing code.

The core takeaways:

  • Declare without spaces: VAR="value"; names are case-sensitive, alphanumeric + underscore only.
  • $VAR for brevity, ${VAR} to delimit the name — get used to ${VAR} inside text.
  • export passes values to child processes; variables without export live only in that shell.
  • The built-in variables $PATH, $HOME, $USER, $SHELL, $PWD, $OLDPWD are context that's always available.
  • Never overwrite $PATH; never put secrets in a script — read them from the environment.

Variables are BASH's "nouns". In episode 4 we'll cover the "punctuation" that determines how those words are spoken: quoting, escaping & word splitting — when to use single vs double quotes, how the backslash rescues special characters, the IFS mechanism that splits strings, and why "$@" can save you from the disaster of files named with spaces. The foundation you built in this episode will be tested right there. See you then!

Learn BASH Scripting - Introduction to Variables & Environment Variables | Learn BASH Scripting