| Tested on | RHEL 10.2 (Coughlan) |
|---|---|
| Package | bash 5.2.26-6.el10 |
| Applies to | Linux distributions with Bash installed |
| Privilege | Normal user; no root required for the examples |
| Scope | Bash variable assignment, naming, $var and ${var} expansion, quoting, empty vs unset, unset, readonly, common declare attributes, and a short scope overview. Includes one basic default-value example; advanced parameter-expansion operators and arrays are covered separately. Does not cover export or environment inheritance in depth. |
| Related guides | Bash shell script tutorial Shell scripting course hub |
In this article, you'll learn how to create Bash variables, expand them with $name and ${name}, and tell the difference between unset, empty, and readonly variables.
Bash variable assignment is simple, but small differences in spacing and quoting can change how a value is interpreted. The examples use printf for predictable output — Bash echo vs printf explains when echo is enough. declare -p makes it easy to check the value and attributes Bash stored.
What Is a Bash Variable?
A Bash variable stores a value under a name such as log_dir or retention_days. Bash documentation uses the broader term parameter; a variable is one type of parameter.
Variables exist in the current shell process. Bash variables do not have fixed data types like variables in many programming languages. Values are normally treated as text, while attributes such as declare -i change how Bash handles assigned values and arithmetic.
Create and Assign Variables
Use name=value with no spaces around =. In a simple assignment, Bash expands variables, commands, arithmetic expressions, and ~, but it does not perform word splitting or filename expansion on the resulting value.
Several expansions can run in the value before it is stored:
user="Ada"
home_copy=~/backup
message="Hello $user"
today=$(date +%F)
printf 'home_copy=%s message=%s today=%s\n' "$home_copy" "$message" "$today"home_copy=/root/backup message=Hello Ada today=2026-09-09$user expanded inside double quotes, ~ became your home directory, and $(date +%F) ran the command and stored its output. Filename expansion does not run on the result of a simple assignment, so an unquoted * in the assigned value is not expanded to matching filenames.
Assign a name that contains a space:
name="Ada Lovelace"
printf 'name=%s\n' "$name"name=Ada LovelaceSpaces around = make Bash try to run a command named name, which fails:
bash -c 'name = value'bash: line 1: name: command not foundNaming rules
Variable names must start with a letter or underscore, then letters, digits, or underscores:
- Valid:
host,_private,log_dir2 - Invalid:
2host(starts with a digit),my-var(hyphen is not allowed in the name)
Lowercase names with underscores are a common convention for script variables, while readonly constants are often written in UPPER_SNAKE_CASE.
Expand Variables with $var and ${var}
Prefix the name with $ to expand its value. $name and ${name} usually do the same thing when the name stands alone.
host=web01
printf 'host is %s\n' "$host"host is web01Use ${name} when the name sits next to other characters. Without braces, Bash reads the longest valid variable name it can:
host=web01
printf 'without braces: %s\n' "$host_backup"
printf 'with braces: %s\n' "${host}_backup"without braces:
with braces: web01_backupThe first line looks empty because Bash searched for a variable called host_backup, not host followed by the literal _backup.
Quote Variable Expansions
Always quote expansions when the value may contain spaces, globs, or an empty string. Double quotes allow parameter expansion and command substitution inside; single quotes do not.
path='/tmp/*.txt'
printf 'path literal: %s\n' "$path"path literal: /tmp/*.txtThe * stayed literal because assignment does not expand globs. If you later use an unquoted $path, Bash may split words or expand the glob when the pattern matches files.
For deeper quoting rules — when to use ', ", and $'…' — see Bash concatenate strings.
Empty vs Unset Variables
An unset variable has no value at all. An empty variable exists but holds "". Those are different states for defaults and strict checks.
Inspect an unset name with declare -p:
unset maybe
declare -p maybebash: declare: maybe: not foundAssign an empty string and check again:
maybe=""
declare -p maybedeclare -- maybe=""Test whether a name is set at all with ${name+x} inside Bash [ ] vs [[ ]] vs (( )):
unset x
[[ -z ${x+x} ]] && echo 'x is unset'
x=""
[[ -z ${x+x} ]] && echo 'x is unset' || echo 'x is set (may be empty)'
[[ -z "$x" ]] && echo 'x is empty string'x is unset
x is set (may be empty)
x is empty string${x+x} expands to nothing when x is unset, so -z ${x+x} is true. After x="", the name is set but -z "$x" reports an empty string.
Change and Unset Variables
Reassign with another name=value line. Remove a name entirely with unset:
retention_days=30
retention_days=7
printf 'retention_days=%s\n' "$retention_days"
unset retention_days
declare -p retention_daysretention_days=7
bash: declare: retention_days: not foundunset deletes the parameter. Assigning "" keeps the name but clears the value — useful when a script must know the variable was intentionally emptied.
Append to an existing string with +=:
server="web"
server+="-01"
printf '%s\n' "$server"web-01Readonly Variables
Mark constants with readonly so later assignment fails:
readonly config_file=/etc/app/config.yaml
config_file=/tmp/other.yamlbash: config_file: readonly variableUse readonly for values that should not change after they are set, such as a configuration path or script-wide constant.
A readonly variable cannot be reassigned or unset for the lifetime of that shell process. declare -r provides the same readonly attribute and can be combined with other declare options.
declare and Variable Attributes
declare (or typeset) creates variables and attaches attributes. Common variable-level options:
| Option | Meaning |
|---|---|
-i |
Integer attribute — assignments are evaluated arithmetically |
-r |
Read-only — same protection as readonly |
-l |
Convert assigned uppercase characters to lowercase |
-u |
Convert assigned lowercase characters to uppercase |
-p |
Display attributes and value (declare -p name) |
Arrays (-a, -A) and namerefs (-n) are separate topics — see split string into array for the first array pattern most scripts need.
For example, declare -i makes assignments arithmetic:
declare -i count=10
count=count+5
printf 'count=%s\n' "$count"count=15Because count has the integer attribute, Bash evaluates count+5 as an arithmetic expression. For $(( )) loops and counters without declare -i, see Bash arithmetic.
Use declare -p name to display the variable's value and attributes:
name="Ada Lovelace"
declare -p namedeclare -- name="Ada Lovelace"The -r flag appears in the output when a name is readonly:
declare -r RO_TEST=1
declare -p RO_TESTdeclare -r RO_TEST="1"Shell Variables, Environment Variables, and Local Variables
A normal assignment creates a shell variable in the current Bash process. Child processes do not see it until you export it — Bash environment variables covers export, inheritance, and the difference from shell-only names.
Local variables inside functions use local. For now, remember:
name=value— current shell onlyexport name— also visible to child processes (see the environment-variables guide above)local name— creates a variable local to the current function call; Bash uses dynamic scoping for functions — see Bash functions
Common Variable Errors
| Symptom | Likely cause | Fix |
|---|---|---|
command not found on assignment |
Spaces around = |
Use name=value with no spaces |
| Empty expansion when a suffix follows | Missing braces | Use ${host}_backup, not $host_backup |
| Word splitting or glob surprises | Unquoted $var |
Quote: "$var" |
readonly variable |
Reassigning a constant | Do not reassign the variable. If you need it writable, remove the readonly or declare -r line and start a new shell or rerun the script |
declare: name: not found |
Variable is unset | Assign a value or test with ${name+x} before use |
Practical Mini-Script
Scripts often read settings from the environment and fall back to defaults. Bash parameter expansion provides two useful forms for defaults:
${VAR:-default}— use the default for this expansion only; the variable stays unset or empty${VAR:=default}— use the default and assign it when the variable is unset or empty
With :-, Bash uses the default value without assigning it:
unset LOG_DIR
printf '%s\n' "${LOG_DIR:-/var/log/myapp}"
declare -p LOG_DIR 2>&1/var/log/myapp
bash: declare: LOG_DIR: not foundThe default printed, but LOG_DIR is still unset. The : "${VAR:=default}" idiom assigns only when the variable is unset or empty:
Save this as app_defaults.sh with a Bash shebang so direct execution picks Bash:
#!/bin/bash
: "${LOG_DIR:=/var/log/myapp}"
: "${RETENTION_DAYS:=7}"
printf 'LOG_DIR=%s RETENTION_DAYS=%s\n' "$LOG_DIR" "$RETENTION_DAYS"Make it executable — Linux file permissions explains the mode bits chmod +x sets — and run it with built-in defaults:
chmod +x app_defaults.sh && ./app_defaults.shLOG_DIR=/var/log/myapp RETENTION_DAYS=7LOG_DIR=/tmp/logs ./app_defaults.sh passes LOG_DIR to that command only. It does not permanently export LOG_DIR in the calling shell:
LOG_DIR=/tmp/logs ./app_defaults.shLOG_DIR=/tmp/logs RETENTION_DAYS=7This example only uses the default-value forms; trimming, replacement, and substring operators use the same ${var} brace syntax covered in Bash compare strings.
Summary
Bash variables are parameters you create with name=value, expand with $name or ${name}, and remove with unset. Spaces around = break assignment, and ${name} is required when letters or digits follow the name.
An unset variable and an empty variable are different, and readonly adds a separate restriction that prevents later changes. Quote expansions when values may be empty or contain spaces, use readonly or declare -r for constants, and run declare -p when you need to see what Bash actually stored.
Environment variables, function-local variables, arrays, and advanced parameter expansion build on the same assignment and expansion rules covered here.
References
- Bash manual — Shell Parameters — parameter attributes and expansion
- Bash manual — Shell Variables — predefined and environment variables
- declare builtin —
declareoptions in GNU Bash
Frequently Asked Questions
Do Bash variables have data types?
Bash variables do not have fixed data types like variables in many programming languages. Values are normally treated as text, while attributes such as declare -i change how Bash handles assigned values and arithmetic.
Why does VAR = value fail?
Bash parses VAR = value as a command named VAR with arguments = and value. Assignment requires VAR=value with no spaces around =.
What is the difference between unset and empty?
An unset variable has no parameter entry. An empty variable exists and holds "". declare -p reports not found for unset names and declare -- name="" for empty ones. Tests such as ${name+x} distinguish the two.
When do I need ${var}?
Use ${var} when characters after the name could be read as part of the variable name (${host}_backup) or when you use parameter-expansion operators (${var:-default}). For a standalone name in double quotes, $var is enough.

