The single most useful parameter expansion is giving a variable a fallback value when it isn't set. You'll write this pattern in every script from now on.
${var:-default} # if var is unset or empty, use "default" (var itself unchanged)
${var:=default} # same, but also assigns "default" to var
${var:?message} # if var is unset or empty, fail with "message"
${var:+alternate} # opposite: if var IS set, use "alternate" instead
Each is a single expression. No braces or if statements needed.
DB_URL="${DB_URL:-postgres://localhost:5432/dev}"
LOG_DIR="${LOG_DIR:-/var/log/myapp}"
PORT="${PORT:-8080}"
Read as: "use $DB_URL if the caller set it, otherwise fall back to the local dev URL". This is how nearly every well-behaved script handles configuration from environment variables.
name=${name:=guest}
echo "$name" # guest
name=${name:=admin}
echo "$name" # guest (still, because it was already set)
:= sets the variable AND returns the value, but only the first
time. Handy in loops where you want to remember what got
assigned.
API_KEY="${API_KEY:?API_KEY must be set before running this script}"
If API_KEY is unset, the script exits with your message. No
if [[ -z ... ]] boilerplate needed. Perfect for production
scripts where a missing value should stop everything before it
causes silent damage.
DEBUG="${DEBUG:+--verbose}"
some-command $DEBUG
If DEBUG is set to anything, the value becomes --verbose. If
unset, the value is empty and the command runs normally. Useful
for optional flags controlled by an env var.
The colon in :-, :=, etc. means "unset OR empty". Without
the colon, only truly unset variables trigger the fallback:
name=""
echo "${name:-guest}" # guest (name is empty, colon triggers fallback)
echo "${name-guest}" # (empty string, name is set even though empty)
Almost always use the colon form. "Empty string" and "unset" usually mean the same thing in a script's mental model.