Bash and Linux

Validate script arguments and exit with a usage code

easySmall scripts

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.

Bash
#!/usr/bin/env bash
# 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=$1
env=$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-run flag using getopts or a while loop with shift.
  • 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.