Control and functions
Branch with if and case
Test command success with if and select a small set of named modes with case.
10 minute lesson
In shell, if doesn’t test truth the way other languages do. It runs a command and branches on its exit status: zero takes the then branch, anything else goes to else. case matches one value against patterns and is clear for command modes.
if runs commands
Because if evaluates a command’s exit status, any command works as a condition:
if grep -q 'server_name' /etc/nginx/nginx.conf; then
printf '%s\n' 'config mentions server_name'
else
printf '%s\n' 'no server_name found' >&2
fi
Tests on strings and files use [[ ... ]], which is itself a command that exits zero when the test holds:
if [[ -f /etc/nginx/nginx.conf && $USER == 'deploy' ]]; then
printf '%s\n' 'ready to check the config'
fi
Prefer [[ ... ]] over the older [ ... ] for Bash-specific scripts: it doesn’t word-split unquoted variables and it supports pattern matching. I still quote arbitrary values in tests — the habit protects you in every other context where quoting does matter.
case for named modes
When a script accepts a small set of subcommands, a chain of elif branches gets noisy fast. Dispatch a mode:
case ${1:-} in
check) printf '%s\n' 'checking' ;;
run) printf '%s\n' 'running' ;;
*) printf 'usage: %s {check|run}\n' "$0" >&2; exit 2 ;;
esac
${1:-} expands to an empty string when no argument was given, so the script behaves under set -u instead of dying with an unbound-variable error. Each branch ends with ;;.
The * pattern is the part people forget. It catches everything the named patterns missed. Leave it out and unknown input matches nothing — the case finishes silently and the script continues as if the argument were fine.
Verify every path
Run every accepted value and one rejected value:
./service-ctl check # checking
./service-ctl run # running
./service-ctl stop # usage: ./service-ctl {check|run}
Each path should have a documented status. After the rejected call, echo $? must print 2 — callers and schedulers branch on that number, not on the usage text.
Lesson completed