Last active
November 13, 2023 10:48
-
-
Save brianclements/5f97039fc25b03be76f17d13b1a05a03 to your computer and use it in GitHub Desktop.
Quick and Dirty Bash Script Template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
##---------------- | |
export _script_name=$(basename -a "$0") | |
export _script_short_desc=" | |
A script for doing things." | |
export _script_mod_date='0000.00.00-00:00' | |
export _script_version='0.0.1' | |
export _script_requirements='none' | |
export _script_bash_debug=false | |
##---------------- | |
shopt -s extglob ## Needed for arg parsing | |
## Internal config | |
export _script_subcommands_args=() | |
export _script_min_args=1 | |
export _script_max_args=1 | |
## Script Config | |
export working_dir=${working_dir:-"$PWD"} | |
# ---------------------------------------------------------------------------------------------- | |
## Script | |
do_work() { | |
echo "doing work in ${working_dir}" | |
} | |
## Args | |
_parse_args() { | |
local _args=("$@") | |
while (( "${#_args}" )); do | |
case "$1" in | |
-@([a-z|A-Z]*)) | |
# Switches | |
_parse_switches "$1" | |
;; | |
--*) | |
# Options | |
_parse_options "$1" | |
;; | |
*) | |
# Subcommands args stored as an array for later. For more | |
# complicated scripts, sometimes we need to act on the | |
# switches/options or source configuration first before we run | |
# the subcommands. | |
_script_subcommands_args=("$@") | |
break | |
;; | |
esac | |
shift | |
_args=("$@") | |
done | |
shopt -u extglob | |
# Check for minimum number of arguments/subcommands beyond switches and options here. | |
if [[ ${#_script_subcommands_args[@]} < $_script_min_args ]]; then | |
echo "Syntax error: a minimum of $_script_min_args arguments required." | |
echo "" | |
_usage_short | |
exit 1 | |
fi | |
if [[ ${#_script_subcommands_args[@]} > $_script_max_args ]]; then | |
echo "Syntax error: only $_script_max_args argument allowed." | |
echo "" | |
_usage_short | |
exit 1 | |
fi | |
} | |
_parse_switches() { | |
local _switches="$1" | |
_switches="${_switches:1}" | |
while (( "${#_switches}" )); do | |
# Handle actions for individual switches here | |
case "${_switches:0:1}" in | |
h) | |
_usage_short | |
exit 0 | |
;; | |
v) | |
_version | |
exit 0 | |
;; | |
*) | |
echo "Error: \"${_switches:0:1}\" not a valid switch" | |
echo "" | |
_usage_short | |
exit 1 | |
;; | |
esac | |
_switches="${_switches:1}" | |
done | |
} | |
_parse_options() { | |
local _option=( $( IFS='='; echo ${1} ) ) | |
local _option_keyword=${_option[0]:2} | |
# Get value of option if there is any | |
local _option_value=${_option[@]:1} | |
# Handle actions for individual options here | |
case "${_option_keyword}" in | |
bash-debug) | |
_script_bash_debug=true | |
;; | |
help) | |
_usage | |
exit 0 | |
;; | |
version) | |
_version | |
exit 0 | |
;; | |
*) | |
echo "Error: \"${_option_keyword}\" not a valid option" | |
echo "" | |
_usage_short | |
exit 1 | |
;; | |
esac | |
} | |
_parse_subcommands() { | |
local _args=("$@") | |
while (( "${#_args}" )); do | |
case "$1" in | |
work) | |
echo "Args passed to work command: ${_args[@]:1}" | |
break | |
;; | |
*) | |
echo "Error: "$1" not a valid subcommand" | |
_usage | |
exit 1 | |
;; | |
esac | |
shift | |
_args=("$@") | |
done | |
} | |
## Dialogs | |
_usage_cli=$(cat <<EOF | |
Usage: ${_script_name} [<switches>...] [<option keyword>=<value>...] <subcommand> <subcommand args> | |
${_script_short_desc} | |
EOF | |
) | |
_usage_args=$(cat <<EOF | |
Switches/Options: | |
--bash-debug Show bash debug info for *most* of the non-template part | |
of the script. It starts after the arguments are parsed. | |
-h|--help Short|Full help | |
-v|--version Version | |
Subcommands | |
work Werk it | |
EOF | |
) | |
_usage_short() { | |
cat <<EOF | |
${_usage_cli} | |
${_usage_args} | |
Run '${_script_name} --help' for the full help documentation. | |
EOF | |
} | |
_usage() { | |
cat <<EOF | |
${_usage_cli} | |
Switches can be combined, or used individually, in any order. Options must | |
follow the format --option="something here" and not have spaces in between | |
the option keyword and it's value. | |
By default: | |
- Any arguments that aren't switches or options are treated as erroneous, but | |
they could potentially be main arguments subcommands. | |
- The script processes all switches and all options, but stops only if | |
"exit" is declared in any of the 'case' statements. This is the current | |
behavior but can be easily changed. | |
${_usage_args} | |
Requirements: | |
${_script_requirements} | |
Version: | |
${_script_name} version: ${_script_version} | |
Last modifed on: ${_script_mod_date} | |
EOF | |
} | |
_version() { | |
echo "${_script_name} version: ${_script_version}" | |
echo "Last modifed on: ${_script_mod_date}" | |
} | |
## Sequence | |
_main() { | |
_parse_args "$@" | |
# I realize this only effects the non-template part of the script; this is usually fine. | |
# If you really want to debug the entire thing, then move `set -x` above `_parse_args "$@"` | |
if $_script_bash_debug; then | |
set -x | |
else | |
set -eo pipefail | |
fi | |
_parse_subcommands "${_script_subcommands_args[@]}" | |
# Default behavior | |
do_work | |
} | |
## Runtime | |
_main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment