Validate script arguments and exit with a usage code
Problem statement
Write deploy.sh, which takes exactly two arguments: a service name and an environment that must be dev, staging or prod. On valid input it prints Deploying <service> to <env> and exits 0. With the wrong number of arguments it prints a usage line to stderr and exits 2. With an unknown environment it prints an error to stderr and exits 2. No sample file is needed; the examples show the exact terminal output (stdout and stderr) for each call.
Examples
Example 1
Input: ./deploy.sh api prod; echo "exit: $?"
Output: Deploying api to prod
exit: 0
Example 2
Input: ./deploy.sh api qa; echo "exit: $?"
Output: Error: unknown environment 'qa'
exit: 2
Example 3
Input: ./deploy.sh api; echo "exit: $?"
Output: Usage: deploy.sh <service> <dev|staging|prod>
exit: 2
Hints
Approach
Optimal
set -euo pipefail makes the script stop on a failed command (-e), on an unset variable (-u) and on a failure anywhere in a pipeline. The argument count is checked with [ "$#" -eq 2 ] before $1 and $2 are read; with -u, reading a missing $2 would otherwise abort with a confusing message. The usage function writes to stderr with >&2, so it never pollutes output that another tool might parse, and exits 2, the usual code for incorrect usage. The case statement whitelists the environment; $(basename "$0") prints the script name however it was invoked.
# Usage: deploy.sh <service> <dev|staging|prod>set -euo pipefail usage() { echo "Usage: $(basename "$0") <service> <dev|staging|prod>" >&2 exit 2} [ "$#" -eq 2 ] || usage service=$1env=$2 case "$env" in dev|staging|prod) ;; *) echo "Error: unknown environment '$env'" >&2; exit 2 ;;esac echo "Deploying $service to $env"Follow-up questions
- Add an optional
--dry-runflag usinggetoptsor awhileloop withshift. - Ask for confirmation (
read -r -p) before deploying to prod, but skip it when stdin is not a terminal.
Frequently asked questions
By convention 1 means 'the job failed' and 2 means 'you called me wrong', which is what grep, diff and most GNU tools use. Distinct codes let a CI pipeline or wrapper script tell a bad invocation apart from a real deploy failure.
Unquoted variables are split on whitespace and glob-expanded. A service name such as my api would become two words, and a value of * would expand to every file in the directory. Quote every expansion unless you want splitting.