Parameter Expansion and Default Values
Bash parameter expansion is the language feature behind many compact shell idioms. Used well, it validates configuration and transforms values without spawning extra processes. Used carelessly, it can make scripts unreadable. This lesson builds the operators from first principles.
Learning objectives
By the end of this lesson
- Differentiate unset variables from null variables in parameter expansion.
- Use default, assignment, alternate, and required-value operators correctly.
- Measure and transform strings with built-in expansion.
- Remove prefixes and suffixes using shell patterns.
- Apply parameter expansion without creating obscure or unsafe code.
1. Parameter expansion happens before a command runs
When Bash sees a word such as "${name:-guest}", it
expands the parameter before the target command receives its
arguments. No external program is required for the basic operation.
flowchart TD
S["Source word: ${NAME:-guest}"] --> E["Bash parameter expansion"]
E --> Q["Quoting and remaining expansions"]
Q --> A["Final command argument"]
The braces make the variable boundary explicit and unlock operators
that cannot be expressed with simple $name.
2. Unset and null are separate states
Suppose name is unset in one case and assigned an empty
string in another. Operators containing a colon, such as
:-, usually treat both unset and null as missing.
Related operators without the colon, such as -, test
only whether the parameter is unset.
unset a
b=""
printf 'a with :- => <%s>\n' "${a:-default}"
printf 'b with :- => <%s>\n' "${b:-default}"
printf 'a with - => <%s>\n' "${a-default}"
printf 'b with - => <%s>\n' "${b-default}"
${p:-word}Use word when p is unset or emptyMost common safe default form
${p-word}Use word only when p is unsetPreserves intentionally empty values
3. Default values do not necessarily modify the variable
${name:-value} substitutes a fallback for that
expansion. It does not assign the fallback to name.
unset REGION
printf 'effective=%s\n' "${REGION:-us-east}"
printf 'stored=%s\n' "${REGION-unset}"
This is ideal when a default is needed for one expression but you do not want to mutate shell state.
4. := assigns a default when the value is missing
The := form both computes a fallback and stores it in
the parameter.
unset CACHE_DIR
printf 'first=%s\n' "${CACHE_DIR:="$HOME/.cache/mytool"}"
printf 'stored=%s\n' "$CACHE_DIR"
Assignment expansion can be concise during configuration initialization. Do not bury complex side effects inside deeply nested expansions; readability matters more than saving a line.
5. :? turns missing configuration into an immediate failure
The required-value form is useful for deployment scripts that cannot operate safely without a specific input.
# Run this in a disposable shell so the example can exit safely.
bash -c '
: "${DEPLOY_ENV:?DEPLOY_ENV must be set and non-empty}"
printf "deploying to %s\n" "$DEPLOY_ENV"
' || printf 'validation failed as expected\n'
DEPLOY_ENV=staging bash -c '
: "${DEPLOY_ENV:?DEPLOY_ENV must be set and non-empty}"
printf "deploying to %s\n" "$DEPLOY_ENV"
'
Validate dangerous deployment inputs before performing any mutation. A clear failure at startup is safer than discovering a missing variable halfway through a rollout.
6. :+ substitutes a value only when a parameter is present
The alternate-value form can conditionally add arguments or labels.
DEBUG=true
printf 'debug flag: %s\n' "${DEBUG:+enabled}"
unset DEBUG
printf 'debug flag: %s\n' "${DEBUG:+enabled}"
For complex conditionals, ordinary if statements are
often clearer. Parameter expansion is strongest when the
transformation remains obvious.
7. Length and substring operations stay inside Bash
Bash can compute string length and extract substrings directly.
commit="a1b2c3d4e5f6"
printf 'length=%s\n' "${#commit}"
printf 'short=%s\n' "${commit:0:7}"
printf 'tail=%s\n' "${commit: -4}"
The space in ${commit: -4} prevents
:- from being interpreted as the default-value
operator.
8. Remove prefixes and suffixes with shell patterns
The # and % forms remove matching prefixes
and suffixes. Single symbols remove the shortest match; doubled
symbols remove the longest.
path="/srv/releases/app.tar.gz"
printf 'short prefix: %s\n' "${path#*/}"
printf 'long prefix: %s\n' "${path##*/}"
file="${path##*/}"
printf 'short suffix: %s\n' "${file%.*}"
printf 'long suffix: %s\n' "${file%%.*}"
Patterns use shell glob syntax, not regular expressions. That
distinction is important when porting logic from grep,
sed, or programming languages.
9. Replace text using parameter expansion
Bash supports pattern replacement without launching
sed for simple cases.
image="registry.example.com/team/api:latest"
printf '%s\n' "${image/latest/2026.08.09}"
text="one-two-two"
printf '%s\n' "${text//two/2}"
The single slash replaces the first match; the double slash replaces all matches. Again, the match expression uses shell pattern rules.
10. Hands-on lab: validate and normalize deployment configuration
Create a script that accepts environment variables, applies defaults, requires one critical value, and derives a normalized label.
mkdir -p "$HOME/devops-academy/bash/chapter02/lesson04"
cd "$HOME/devops-academy/bash/chapter02/lesson04"
cat > config.sh <<'EOF'
#!/usr/bin/env bash
: "${APP_NAME:?APP_NAME is required}"
DEPLOY_ENV=${DEPLOY_ENV:-development}
REGION=${REGION:-eu-central-1}
IMAGE_TAG=${IMAGE_TAG:-latest}
safe_name=${APP_NAME// /-}
safe_name=${safe_name,,}
printf 'app=%s\n' "$safe_name"
printf 'environment=%s\n' "$DEPLOY_ENV"
printf 'region=%s\n' "$REGION"
printf 'image_tag=%s\n' "$IMAGE_TAG"
EOF
APP_NAME="Example API" bash config.sh
APP_NAME="Example API" DEPLOY_ENV=staging IMAGE_TAG=abc1234 bash config.sh
Verification checklist
11. Knowledge check
Question 1. What is the difference between
${p:-x} and ${p-x}?
Question 2. Which form is useful for a required non-empty deployment variable?
${VAR:?message}, commonly used in a no-op command
such as : "${VAR:?message}".
Question 3. Do #/%
removals use regular expressions?
12. Summary
Parameter expansion is Bash's built-in toolkit for defaults, validation, string length, extraction, prefix/suffix removal, and lightweight replacement. The most important production distinction is between unset and empty values. Use concise expansions when they make intent clearer, and switch to explicit control flow when compact syntax begins hiding behavior.
13. Further reading
- GNU Bash Reference Manual — Shell Parameter Expansion.
- POSIX Shell Command Language — parameter expansion operators.
- ShellCheck wiki — quoting, undefined variables, and expansion pitfalls.
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.