#!/usr/bin/env bash set -euo pipefail SELF=$(basename "$0") COLORS=0 VERBOSE=1 POSITIONALS=() POSITIONALS_REQUIRED=2 function usage { >&2 cat <<HELP Usage: $SELF [options] input_file output_dir Options: -h, --help Display help, exit -v, --verbose Be more verbose -c, --color Use colors HELP } function main { local input_file="$1"; shift local output_dir="$1"; shift # business logic here... } # Business logic functions here ... # # Helpers # function log_info { [[ $COLORS -eq 1 ]] \ && echo -e "\e[32m[info] $1\e[0m" \ || echo "[info] $1" } function log_debug { [[ $VERBOSE -eq 0 ]] \ || >&2 echo "[debug] $1" } function log_error { [[ $COLORS -eq 1 ]] \ && >&2 echo -e "\e[31m[error] $1\e[0m" \ || >&2 echo "[error] $1" } function exit_usage { local code="${1:-99}"; shift || : local error_msg="${1:-}"; shift || : [[ -z "$error_msg" ]] || log_error "$error_msg" usage exit "$code" } # # Main plumbing # function parse_args { local arg="${1:-}"; shift || : case "$arg" in # Known flags -h|--help) exit_usage 1 ;; -v|--verbose) VERBOSE=1 ;; -s|--silent) VERBOSE=0 ;; -c|--color) COLORS=1 ;; # Unknown flag -*) exit_usage 5 "unknown flag: $arg" ;; # Positional args *) POSITIONALS[${#POSITIONALS[@]}]="$arg" ;; esac # Recurse to the next arg [[ $# -lt 1 ]] || parse_args $@ } parse_args $@ [[ ${#POSITIONALS[@]} -eq $POSITIONALS_REQUIRED ]] \ || exit_usage 3 "expecting $POSITIONALS_REQUIRED args, but got ${#POSITIONALS[@]}" main ${POSITIONALS[@]}