Bash template. Probably appropriate for moderately or more involved scripts.
#!/bin/bash
# Description.
a_function () {
# Function description.
# Arguments
# $1 Description.
# $2 Really
# long description.
local arg1="$1"
local -i arg2="$2"
# Function body.
}
configure() {
# Configure the process.
# The order of configuration:
# - Script global configuration variable declaration and initialization.
# - Configuration files.
# - System configuration file.
# - User configuration file.
# - Special purpose configuration file.
# - Environment variables overriding variable assignments in script or
# configuration files. The value of the environment variable should only
# be relevant at the time of configuration.
# - Command line arguments.
# Arguments
# $1 Script file path.
# $2 Script command line arguments.
# Declare and initialize global configuration variables.
# Readonly string requiring no option, of course:
declare -gr var_readonly="value_var_readonly"
# Flag with default value of true:
declare -gi var_bool=0
# Flag with default value of false:
#declare -gi var_bool=1
# String with required value:
declare -g var_required="some value"
# String with required value, not null:
declare -g var_required_not_null="some value"
# Allow environment variable to override assignement.
declare -g var_env_override="${ENV_VAR:-"some value"}"
# Flag with optional, associated value.
declare -gi var_bool_optional_value=0
declare -g var_bool_optional_associated_value="some value"
# Canonicalize arguments with getopt.
local script_file="$1"
shift 1
local args=
args="$(getopt --name "$script_file" --options bBr:n:e:o::O \
--longoptions conf-file:,no-sys-conf-file,no-usr-conf-file \
\
--longoptions var-bool,no-var-bool \
--longoptions var-required: \
--longoptions var-required-not-null: \
--longoptions var-env-override: \
--longoptions var-bool-optional-value::,no-var-bool-optional-value \
\
-- "$@")" \
|| return
# Evaluate configuration file options to determine which files will be
# applied, if any.
# Configuration file variables.
local -r conf_filename_base="$(basename "$script_file")"
local -r sys_conf_file="/etc/$conf_filename_base"
local -r usr_conf_file=~/".$conf_filename_base.conf"
local conf_file=
eval set -- "$args"
while true; do
case $1 in
# Don't read the system configuration file.
--no-sys-conf-file )
sys_conf_file=
shift
;;
# Don't read the user configuration file.
--no-usr-conf-file )
usr_conf_file=
shift
;;
# Special purpose configuration file.
--conf-file )
conf_file="${2:?"Option '$1' requires a non-null argument."}"
shift 2
;;
# Skip non-option arguments and break when done.
-- )
shift
until [[ $# -eq 0 ]]; do
shift
done
break
;;
# Skip other options and option arguments.
* )
shift
;;
esac
done
# Source system configuration file.
[[ -f $sys_conf_file ]] && {
[[ $sys_conf_file == \
$(find "$sys_conf_file" -maxdepth 0 -type f -user root \
! -perm /022) ]] || {
printf "%s: System configuration file '%s' must be owned by root and writable by no user other than root.\n" \
"$FUNCNAME" "$sys_conf_file" >&2
return 1
}
source "$sys_conf_file" || return
}
# Source user configuration file.
[[ -f $usr_conf_file ]] && {
[[ -O $usr_conf_file && $usr_conf_file == \
$(find "$usr_conf_file" -maxdepth 0 ! -perm /022) ]] || {
printf "%s: User configuration file '%s' must be owned by the user and writable by no user other than the user\n" \
"$FUNCNAME" "$usr_conf_file" >&2
return 1
}
source "$usr_conf_file" || return
}
# Source user special configuration file.
[[ $conf_file ]] && {
[[ -f $conf_file && -O $conf_file && $conf_file == \
$(find "$conf_file" -maxdepth 0 ! -perm /022) ]] || {
printf "%s: User special configuration file '%s' must exist, be owned by the user, and be writable by no user other than the user.\n" \
"$FUNCNAME" "$conf_file" >&2
return 1
}
source "$conf_file" || return
}
# Evaluate the remainder of commnad line arguments.
local -a non_option_args=()
eval set -- "$args"
while true; do
case $1 in
# Skip configuration file options and option arguments as they have
# already been evaluated.
--conf-file ) shift 2 ;;
--no-sys-conf-file | --no-usr-conf-file) shift 1 ;;
# Process options, option arguments, and non-option arguments.
# Flag
-b | --var-bool )
var_bool=0
shift
;;
-B | --no-var-bool )
var_bool=1
shift
;;
# Required value:
-r | --var-required )
var_required="$2"
shift 2
;;
# Required value, not null:
-n | --var-required-not-null )
var_required_not_null=\
"${2:?"Option '$1' requires a non-null argument."}"
shift 2
;;
# Environment variable overrides configuration, not command line
# arguments.
-e | --var-env-override )
var_env_override="$2"
shift 2
;;
# Flag with optional value.
-o | --var-bool-optional-value )
var_bool_optional_value=0
var_bool_optional_associated_value="$2"
shift 2
;;
-O | --no-var-bool-optional-value )
var_bool_optional_value=1
# Ignore null in $2.
shift 2
;;
-- )
shift
until [[ $# -eq 0 ]]; do
non_option_args+=("$1")
shift
done
break
;;
# In case we forget to handle an option.
-? | --* )
printf "%s: Option '%s' is unhandled.\n" "$FUNCNAME" "$1" >&2
return 1
;;
esac
done
}
main () {
# Call to this function is entry point.
# Arguments
# $1 Script file path.
# $2 Command line arguments.
configure "$@" || return
# Body, function calls.
}
main "$0" "$@"