Reference Code Blocks¶
Reusable code blocks for complex Bash scripts. Pick and compose only the sections a script actually needs, and do not copy everything blindly.
See developing-complex-bash-scripts.md for the composition guide.
1. Script Identity¶
2. Logging Subsystem¶
Use this block when the script needs more than simple echo output. The setter functions (set_log_level, set_log_format) are included as part of this block - omit them only if the script does not expose --log-level / --log-format flags.
# Logging configuration
declare -g LOG_LEVEL="INFO" # ERROR, WARNING, INFO, DEBUG
declare -g LOG_FORMAT="simple" # simple, level, full
# Log level priorities
declare -g -A LOG_PRIORITY=(
["DEBUG"]=10
["INFO"]=20
["WARNING"]=30
["ERROR"]=40
["CRITICAL"]=50
)
log_color() {
local color="${1}"
shift
if [[ -t 2 ]]; then
printf "\x1b[0;%sm%s\x1b[0m\n" "${color}" "${*}" >&2
else
printf "%s\n" "${*}" >&2
fi
}
log_message() {
local color="${1}"
local level="${2}"
shift 2
if [[ "${LOG_PRIORITY[$level]}" -lt "${LOG_PRIORITY[$LOG_LEVEL]}" ]]; then
return 0
fi
local message="${*}"
case "${LOG_FORMAT}" in
simple) log_color "${color}" "${message}" ;;
level) log_color "${color}" "[${level}] ${message}" ;;
full) log_color "${color}" "[$(date --utc --iso-8601=seconds)][${level}] ${message}" ;;
*) log_color "${color}" "${message}" ;;
esac
}
log_error() { log_message 31 "ERROR" "${@}"; }
log_info() { log_message 32 "INFO" "${@}"; }
log_warning() { log_message 33 "WARNING" "${@}"; }
log_debug() { log_message 34 "DEBUG" "${@}"; }
log_critical() { log_message 36 "CRITICAL" "${@}"; }
set_log_level() {
local level="${1^^}"
if [[ -z "${LOG_PRIORITY[${level}]:-}" ]]; then
log_error "Invalid log level: ${1}. Valid levels: ERROR, WARNING, INFO, DEBUG"
exit 1
fi
LOG_LEVEL="${level}"
}
set_log_format() {
case "${1}" in
simple | level | full)
LOG_FORMAT="${1}"
;;
*)
log_error "Invalid log format: ${1}. Valid formats: simple, level, full"
exit 1
;;
esac
}
3. Dependency Check¶
Include this when the script relies on external commands that may not be present. It calls log_error, so include the Logging Subsystem first.
require_command() {
local missing=()
for c in "${@}"; do
if ! command -v "${c}" >/dev/null 2>&1; then
missing+=("${c}")
fi
done
if [[ ${#missing[@]} -gt 0 ]]; then
log_error "Required command(s) not installed: ${missing[*]}"
log_error "Please install the missing dependencies and try again"
exit 1
fi
}
4. Cleanup Handler¶
Include this when the script creates temporary files or resources that must be cleaned up on exit.
cleanup() {
local exit_code=$?
# Add cleanup logic here (e.g., rm -rf "${TMPDIR:-}")
exit "${exit_code}"
}
trap cleanup EXIT INT TERM
5. Usage / Help¶
Adapt the OPTIONS and EXAMPLES sections to the actual flags of the script.
Argument ordering convention: in the OPTIONS block, place template flags first, then script-specific flags. This mirrors the ordering required in longoptions and the case statement (see Section 6), keeping all three in sync.
usage() {
local exit_code="${1:-0}"
cat <<EOF
USAGE:
${SCRIPT_NAME} [OPTIONS]
Short description of what the script does.
OPTIONS:
-h, --help Show this help message
--log-level LEVEL Set log level (ERROR, WARNING, INFO, DEBUG)
Default: INFO
--log-format FORMAT Set log output format (simple, level, full)
simple: message only
level: [LEVEL] message
full: [timestamp][LEVEL] message
Default: simple
<script-specific flags go here>
EXAMPLES:
${SCRIPT_NAME} --help
${SCRIPT_NAME} --log-level DEBUG --log-format full
EOF
exit "${exit_code}"
}
6. Argument Parsing¶
MUST use getopt, not getopts, to support long options. Adjust options/longoptions to match the script's flags.
Argument ordering convention: place infrastructure/template flags first and script-specific flags after - in both the longoptions string and the case statement. This keeps the interface predictable and the case block easy to scan.
longoptions="help,log-level:,log-format:,<script-flag-1>:,<script-flag-2>:"
^--- template flags first ---^ ^--- script flags after ---^
parse_args() {
local args
local options="h"
local longoptions="help,log-level:,log-format:"
if ! args=$(getopt --options="${options}" --longoptions="${longoptions}" --name="${SCRIPT_NAME}" -- "${@}"); then
usage 1
fi
eval set -- "${args}"
declare -g -a REST_ARGS=()
while true; do
case "${1}" in
# --- template flags (always first) ---
-h | --help)
usage 0
;;
--log-level)
set_log_level "${2}"
shift 2
;;
--log-format)
set_log_format "${2}"
shift 2
;;
# --- script-specific flags ---
--)
shift
break
;;
*)
log_error "Unexpected option: ${1}"
usage 1
;;
esac
done
REST_ARGS=("${@}")
}