100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Bash

Advanced Parameter Expansion

Master Bash's ${...} parameter expansion operators for defaults, substring removal, pattern substitution, case conversion, and length/indirection tricks that eliminate the need for external tools like sed and awk in many scripts.

Advanced FundamentalsAdvanced11 min readJul 10, 2026
Analogies

Defaults, Errors, and Alternate Values

Bash's ${VAR:-default} expands to default only if VAR is unset or empty, without modifying VAR itself, while ${VAR:=default} does the same but also assigns default back into VAR; ${VAR:?message} exits the script with message printed to stderr if VAR is unset or empty, which is invaluable for validating required arguments early; and ${VAR:+alt} expands to alt only if VAR IS set and non-empty, useful for conditionally appending flags.

🏏

Cricket analogy: A team's reserve batsman only walks in (${VAR:-default}) if the regular opener is unavailable, without replacing them permanently on the team sheet, whereas officially transferring the reserve onto the playing eleven mirrors ${VAR:=default} persisting the assignment.

bash
#!/usr/bin/env bash
set -euo pipefail

# :- gives a default without modifying the variable
port="${PORT:-8080}"

# := assigns the default back into the variable
: "${LOG_LEVEL:=info}"
echo "Log level is now permanently set to: $LOG_LEVEL"

# :? exits with an error message if unset/empty
db_host="${DB_HOST:?Error: DB_HOST must be set}"

# :+ expands only if the variable IS set and non-empty
verbose_flag="${VERBOSE:+--verbose}"
curl $verbose_flag "https://$db_host:$port/health"

Substring Removal and Pattern Substitution

The pattern-removal operators ${VAR#pattern} and ${VAR##pattern} strip the shortest and longest matching prefix respectively, while ${VAR%pattern} and ${VAR%%pattern} strip the shortest and longest matching suffix — extremely common for stripping file extensions or directory paths without spawning basename/dirname subprocesses. Pattern substitution with ${VAR/pattern/replacement} replaces the first match and ${VAR//pattern/replacement} replaces all matches, giving sed-like find-and-replace power natively in the shell.

🏏

Cricket analogy: Trimming a bowler's long run-up down to a shorter approach for a T20 over is like ${VAR%pattern} trimming a trailing suffix, while completely removing the run-up for a rapid bowl-out mirrors ${VAR%%pattern} stripping the longest possible suffix match.

Mnemonic: # removes from the front (think of # as pointing left toward the start), % removes from the back (think of % as trailing off toward the end). Doubling the operator (## or %%) makes the match greedy (longest possible), while a single # or % is non-greedy (shortest possible match).

bash
path="/var/log/app/access.log.gz"

echo "${path##*/}"        # access.log.gz  (strip longest prefix up to last /)
echo "${path%.gz}"        # /var/log/app/access.log (strip .gz suffix)
echo "${path%%.*}"        # /var/log/app/access (strip longest suffix from first .)

filename="IMG_2026_beach_vacation.jpg"
echo "${filename/beach/mountain}"   # replace first match
echo "${filename//_/-}"             # replace ALL underscores with hyphens

Length, Case Conversion, and Indirection

${#VAR} returns the character length of a string (or element count for an array), which is handy for input validation without calling wc or expr. Bash 4+ added case-conversion operators: ${VAR^} and ${VAR^^} uppercase the first character or the whole string respectively, while ${VAR,} and ${VAR,,} lowercase the first character or whole string. Indirect expansion with ${!VAR} treats VAR's value as the name of another variable and expands that variable instead, enabling a form of dynamic variable referencing without eval.

🏏

Cricket analogy: ${#VAR} checking a string's length is like the scoreboard instantly reporting the number of runs scored without a manual recount; ${VAR^^} uppercasing a whole team abbreviation like 'ind' to 'IND' mirrors the broadcast graphics standardizing team codes for the scoreboard.

${!VAR} indirect expansion and eval-based dynamic variable access are powerful but can make scripts harder to read and reason about, and if VAR's value ever comes from untrusted input, indirection combined with eval can become a code-injection vector. Prefer associative arrays (covered later in this course) over indirect variable naming whenever you need a genuine key-to-value mapping — it is safer and clearer.

  • ${VAR:-default} and ${VAR:=default} supply fallback values, but only := actually persists the default into VAR.
  • ${VAR:?message} enforces required variables by exiting with an error if VAR is unset or empty.
  • ${VAR:+alt} expands only when VAR is set and non-empty, useful for conditional flags.
  • # and ## strip prefixes (shortest/longest match); % and %% strip suffixes (shortest/longest match).
  • ${VAR/pat/repl} replaces the first match; ${VAR//pat/repl} replaces all matches, like sed's g flag.
  • ${#VAR} returns string length; ${VAR^^}/${VAR,,} convert case without external tools like tr.
  • ${!VAR} performs indirect expansion, but should be used sparingly in favor of associative arrays for clarity and safety.

Practice what you learned

Was this page helpful?

Topics covered

#Bash#AdvancedBashScriptingStudyNotes#DevOps#AdvancedParameterExpansion#Advanced#Parameter#Expansion#Defaults#StudyNotes#SkillVeris