Chapter 11Lesson 02~85 minutes

set -u, nounset, and Safer Variable Access

`set -u` catches misspelled or forgotten variables, but optional configuration and positional arguments still need deliberate handling. Safe strict-mode code distinguishes unset, empty, optional, and required values.

IntermediateError handlingHands-on lab

Learning objectives

By the end of this lesson

  • Explain unset versus empty values.
  • Use default and required parameter expansion.
  • Read positional arguments safely.
  • Check missing array/map elements.
  • Normalize optional configuration early.

1. nounset turns unset expansion into an error

set -u

printf '%s\n' "$NOT_DEFINED"

set -u (or set -o nounset) helps catch misspelled variable names and forgotten initialization by treating many unset expansions as errors.

2. Unset and empty are different states

set -u

empty=""
printf 'empty=<%s>\n' "$empty"

# unset_value is not defined:
# printf '%s\n' "$unset_value"

An explicitly empty string is still set. This distinction is important in configuration precedence and optional values.

3. Parameter expansion provides safe defaults

set -u

environment=${DEPLOY_ENV:-staging}
printf 'environment=%s\n' "$environment"

${var:-default} uses the default when the variable is unset or empty. The no-colon form ${var-default} only substitutes when the variable is unset.

4. Required-variable expansion can fail with a message

set -u

: "${API_TOKEN:?API_TOKEN must be set}"
: "${DEPLOY_ENV:=staging}"

printf 'env=%s\n' "$DEPLOY_ENV"

:? is useful for required configuration and := can assign a default. Keep messages actionable and avoid echoing secrets.

5. Missing positional parameters need guarded access

set -u

service=${1:-}
if [[ -z $service ]]; then
  printf 'usage: %s SERVICE\n' "$0" >&2
  exit 64
fi

Directly expanding $1 under nounset when no argument exists can terminate the script before your usage message runs.

6. Arrays and missing elements require deliberate access

set -u

items=("api" "worker")
printf 'first=%s\n' "${items[0]}"

if [[ -v 'items[5]' ]]; then
  printf 'fifth=%s\n' "${items[5]}"
fi

Use [[ -v expression ]] when you need to know whether a specific variable or array element is set.

7. Associative-array missing keys are another nounset boundary

set -u
declare -A config=([environment]=staging)

if [[ -v 'config[region]' ]]; then
  region=${config[region]}
else
  region=eu-central-1
fi

printf 'region=%s\n' "$region"

8. Indirect and dynamically named variables deserve extra care

set -u

name='DEPLOY_ENV'
value=${!name-}
printf 'value=<%s>\n' "$value"

Dynamic variable access can make nounset failures harder to read. Prefer ordinary variables or associative arrays when possible.

9. Optional environment variables should be normalized early

set -u

verbose=${VERBOSE:-false}
region=${REGION:-eu-central-1}
timeout_seconds=${TIMEOUT_SECONDS:-30}

Normalize optional inputs near startup so later code can work with defined variables and clearer invariants.

10. Local variables should be initialized before use

set -u

deploy() {
  local service=${1:-}
  local environment=${2:-staging}

  [[ -n $service ]] || {
    printf 'service required\n' >&2
    return 64
  }

  printf 'service=%s env=%s\n' "$service" "$environment"
}

11. Hands-on lab: nounset-safe configuration

mkdir -p "$HOME/devops-academy/bash/chapter11/lesson02"
cd "$HOME/devops-academy/bash/chapter11/lesson02"

cat > config-demo.sh <<'EOF'
#!/usr/bin/env bash
set -u

service=${1:-}
environment=${DEPLOY_ENV:-staging}
replicas=${REPLICAS:-2}

if [[ -z $service ]]; then
  printf 'usage: %s SERVICE\n' "$0" >&2
  exit 64
fi

[[ $replicas =~ ^[0-9]+$ ]] || {
  printf 'REPLICAS must be numeric\n' >&2
  exit 65
}

printf 'service=%s env=%s replicas=%s\n' \
  "$service" "$environment" "$replicas"
EOF

bash config-demo.sh api
DEPLOY_ENV=prod REPLICAS=3 bash config-demo.sh worker
bash config-demo.sh || true

Verification checklist

12. Knowledge check

Question 1. What does nounset catch?

Question 2. What is the difference between ${v:-x} and ${v-x}?

Question 3. How should missing positional parameters be read under nounset?

Question 4. Why normalize optional environment variables early?

13. Summary

set -u catches an important class of shell bugs, but optional values must be handled intentionally. Guard positional parameters, distinguish unset from empty, initialize locals, and normalize configuration near the start of the script.

14. Further reading

  • GNU Bash Reference Manual — nounset.
  • GNU Bash Reference Manual — Shell Parameter Expansion.
  • GNU Bash Reference Manual — Conditional Expressions and -v.
  • ShellCheck documentation — unset-variable patterns.
Next lesson

pipefail and Reliable Pipeline Failure Detection

Continue Chapter 11 by making Bash failure behavior more explicit and predictable.

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.