Reliable automation

Load configuration deliberately

Separate defaults, environment input, and arguments without executing an untrusted configuration file.

10 minute lesson

~~~

A script gets its settings from three places: defaults baked into the script, environment variables, and arguments. The tempting fourth is source config.sh — but shell source executes a file as code. Anyone who can edit that file owns every run of your script, with your permissions. For simple configuration, prefer environment variables or parse a constrained format.

Environment variables with explicit policy

Bash parameter expansion states requirements and defaults in one line each. Require a destination with a default interval:

backup_destination=${BACKUP_DESTINATION:?set BACKUP_DESTINATION}
backup_interval=${BACKUP_INTERVAL:-daily}
printf 'destination=%s interval=%s\n' "$backup_destination" "$backup_interval"

${VAR:?message} stops the script with your message when the variable is unset or empty. ${VAR:-default} substitutes a fallback instead. Between the two forms, the script itself documents which settings are required and which have defaults — no separate README to drift out of date.

Verify the three cases

Run with missing, valid, and empty values:

./backup.sh
# ./backup.sh: line 3: BACKUP_DESTINATION: set BACKUP_DESTINATION

BACKUP_DESTINATION=/mnt/backups ./backup.sh
# destination=/mnt/backups interval=daily

BACKUP_DESTINATION= ./backup.sh
# ./backup.sh: line 3: BACKUP_DESTINATION: set BACKUP_DESTINATION

The empty case matters. The colon in :? makes an empty value count as missing — which is what you want, since an empty destination is as useless as none. Without the colon (${VAR?msg}), an empty string would pass the check and fail somewhere deeper.

Validate before use

A value that arrived is not yet a value you can trust:

case $backup_interval in
  hourly|daily|weekly) ;;
  *) printf 'invalid BACKUP_INTERVAL: %s\n' "$backup_interval" >&2; exit 2 ;;
esac

Validate paths and allowed modes before use. A typo like BACKUP_INTERVAL=dialy should fail loudly at startup, not quietly select some accidental behavior three functions deep.

Two closing rules. Do not print secret configuration — paths and modes are fine to echo, tokens and passwords are not, because logs outlive credential rotations. And if you do adopt a config file one day, treat it as data to parse line by line, never as code to execute.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →