Lesson 6 / 25
Parameter Expansion, Command Substitution and Arithmetic
Transform strings and compute values without external tools.
Built-in string and number operations
Bash can transform variables without calling sed or cut. Defaults: ${var:-fallback} uses a fallback if var is unset or empty; ${var:=fallback} also assigns it; ${var:?message} exits with an error if it is missing, a neat way to require variables. Length: ${#var}. Removing prefixes and suffixes: ${path#*/} removes the shortest match of */ from the start, ${path##*/} the longest (giving the file name), ${file%.*} removes the shortest suffix match (dropping an extension), ${file%%.*} the longest. Replacement: ${var/old/new} replaces the first match, ${var//old/new} all matches. Substrings: ${var:offset:length}. Case (Bash 4+): ${var^^} upper, ${var,,} lower. Command substitution $(command) captures a command's output (trailing newlines removed); prefer it to old backticks. Arithmetic expansion $(( expression )) does integer maths: $(( count + 1 )), $(( size / 1024 )); Bash has no floating point, so use awk or bc when you need decimals.
Expansions in practice
Each line uses a built-in expansion instead of an external command.
path="/var/backups/db/shop-2026-10-03.sql.gz"
echo "${path##*/}" # shop-2026-10-03.sql.gz (basename)
echo "${path%/*}" # /var/backups/db (dirname)
name="${path##*/}"
echo "${name%%.*}" # shop-2026-10-03 (strip all extensions)
echo "${name/2026/2027}" # replace first match
: "${DB_HOST:?DB_HOST must be set}" # exit with a message if missing
port="${DB_PORT:-5432}"
files=$(ls /var/backups/db | wc -l)
echo "backups: $files, next number: $(( files + 1 ))"
echo "upper: ${name^^}"Prefer built-ins in loops
Calling basename, dirname or sed once per iteration over thousands of files starts thousands of processes. Parameter expansion does the same work inside Bash, often dozens of times faster.
Quick check: With file="report.tar.gz", what does `${file%.*}` produce?
- report.tar
- report
- tar.gz
- gz
Answer
report.tar — % removes the shortest matching suffix, so only ".gz" is removed.