Last active
July 30, 2022 21:42
-
-
Save drmikecrowe/2f1db57d519a46bbab9c9b10f90c8e9e to your computer and use it in GitHub Desktop.
Bash Template
This file contains hidden or 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
#!/usr/bin/env bash | |
function mainScript() { | |
debug "Arguments: $args" | |
} | |
# Options and Usage | |
# ----------------------------------- | |
function usage() { | |
echo -n "${scriptName} [OPTION]... | |
${bold}Options:${reset} | |
-l, --log-level Set the display logging level (default=${bold}${logLevel}${reset}. Valid values are debug|info|notice | |
-d, --debug Set logging level to debug (shortcut) | |
-n, --notice Set logging level to notice (shortcut) | |
-h, --help Display this help and exit | |
--version Output version information and exit | |
" | |
} | |
function process_user_options() { | |
# Print help if no arguments were passed. | |
# Uncomment to force arguments when invoking the script | |
# ------------------------------------- | |
#[[ $# -eq 0 ]] && set -- "--help" | |
# Set Flags | |
quiet=false | |
printLog=false | |
logLevel=info | |
force=false | |
strict=false | |
debug=false | |
readme=true | |
output=true | |
args=() | |
# Read the options and set stuff | |
while [[ ${1} = -?* ]]; do | |
case ${1} in | |
-h|--help) usage >&2; safeExit ;; | |
--version) echo "$(basename ${0}) ${version}"; safeExit ;; | |
-l|--log-level) shift; logLevel=${1}; ;; | |
-d|--debug) logLevel=debug; ;; | |
-n|--notice) logLevel=notice; ;; | |
--endopts) break ;; | |
*) usage; die "invalid option: '${1}'." ;; | |
esac | |
shift | |
done | |
# Store the remaining part as arguments. | |
args+=("$@") | |
} | |
### | |
### ----------------------[ No editing normally below here ]---------------------- | |
### | |
# Define log levels | |
# ---------------------- | |
declare -A logLevels=(["debug"]=0 ["info"]=1 ["notice"]=2) | |
declare -A cache=() | |
# Set Base Variables | |
# ---------------------- | |
scriptName=$(basename "${0}") | |
# Logging | |
# ----------------------------------- | |
# Log is only used when the '-l' flag is set. | |
logFile="/tmp/${scriptBasename}.log" | |
function trapCleanup() { | |
echo "" | |
# Delete temp files, if any | |
if [ -d "${tmpDir}" ] ; then | |
rm -r "${tmpDir}" | |
fi | |
die "Exit trapped. In function: '${FUNCNAME[*]}'" | |
} | |
function safeExit() { | |
# Delete temp files, if any | |
if [ -d "${tmpDir}" ] ; then | |
rm -r "${tmpDir}" | |
fi | |
trap - INT TERM EXIT | |
exit | |
} | |
# Set Colors | |
bold=$(tput bold) | |
reset=$(tput sgr0) | |
purple=$(tput setaf 171) | |
red=$(tput setaf 1) | |
green=$(tput setaf 76) | |
tan=$(tput setaf 3) | |
blue=$(tput setaf 38) | |
underline=$(tput sgr 0 1) | |
# Set Temp Directory | |
tmpDir="/tmp/${scriptName}.${RANDOM}.${RANDOM}.${RANDOM}.$" | |
(umask 077 && mkdir "${tmpDir}") || { | |
die "Could not create temporary directory! Exiting." | |
} | |
# Logging & Feedback | |
# ----------------------------------------------------- | |
function _alert() { | |
case ${1} in | |
debug|notice|info) | |
[[ ${logLevels[${1}]} ]] || return 1 | |
#check if level is enough | |
(( ${logLevels[${1}]} < ${logLevels[$logLevel]} )) && return 2 | |
;; | |
esac | |
case ${1} in | |
error) local color="${bold}${red}"; ;; | |
warning) local color="${red}"; ;; | |
success) local color="${green}"; ;; | |
debug) local color="${purple}"; ;; | |
header) local color="${bold}${tan}"; ;; | |
input) local color="${bold}"; ;; | |
notice) local color="${green}"; ;; | |
info) local color=""; ;; | |
esac | |
# Don't use colors on pipes or non-recognized terminals | |
if [[ "${TERM}" != "xterm"* ]] || [ -t 1 ]; then color=""; reset=""; fi | |
# Print to console when script is not 'quiet' | |
if ${quiet}; then return; else | |
local _brk="\\\n" | |
if [ "${1}" == "input" ]; then _brk=""; fi | |
echo -e "$(date +"%r") ${color}$(printf "[%7s]" "${1}") ${_message}${reset}${_brk}"; | |
fi | |
# Print to Logfile | |
if ${printLog} && [ "${1}" != "input" ]; then | |
color=""; reset="" # Don't use colors in logs | |
echo -e "$(date +"%m-%d-%Y %r") $(printf "[%7s]" "${1}") ${_message}" >> "${logFile}"; | |
fi | |
} | |
function die() { local _message="${*} Exiting."; quiet=false; echo -e "$(_alert error)"; safeExit;} | |
function error() { local _message="${*}"; echo -en "$(_alert error)"; } | |
function warning() { local _message="${*}"; echo -en "$(_alert warning)"; } | |
function notice() { local _message="${*}"; echo -en "$(_alert notice)"; } | |
function info() { local _message="${*}"; echo -en "$(_alert info)"; } | |
function debug() { local _message="${*}"; echo -en "$(_alert debug)"; } | |
function success() { local _message="${*}"; echo -en "$(_alert success)"; } | |
function input() { local _message="${*}"; echo -en "$(_alert input)"; } | |
function header() { local _message="== ${*} == "; echo -e "$(_alert header)"; } | |
function verbose() { if ${verbose}; then debug "$@"; fi } | |
# SEEKING CONFIRMATION | |
# ------------------------------------------------------ | |
function seek_confirmation() { | |
input "$@" | |
if "${force}"; then | |
notice "Forcing confirmation with '--force' flag set" | |
else | |
read -p " (y/n) " -n 1 | |
echo "" | |
fi | |
} | |
function is_confirmed() { | |
if "${force}"; then | |
return 0 | |
else | |
if [[ "${REPLY}" =~ ^[Yy]$ ]]; then | |
return 0 | |
fi | |
return 1 | |
fi | |
} | |
function is_not_confirmed() { | |
if "${force}"; then | |
return 1 | |
else | |
if [[ "${REPLY}" =~ ^[Nn]$ ]]; then | |
return 0 | |
fi | |
return 1 | |
fi | |
} | |
# Iterate over options breaking -ab into -a -b when needed and --foo=bar into | |
# --foo bar | |
optstring=h | |
unset options | |
while (($#)); do | |
case ${1} in | |
# If option is of type -ab | |
-[!-]?*) | |
# Loop over each character starting with the second | |
for ((i=1; i < ${#1}; i++)); do | |
c=${1:i:1} | |
# Add current char to options | |
options+=("-$c") | |
# If option takes a required argument, and it's not the last char make | |
# the rest of the string its argument | |
if [[ $optstring = *"$c:"* && ${1:i+1} ]]; then | |
options+=("${1:i+1}") | |
break | |
fi | |
done | |
;; | |
# If option is of type --foo=bar | |
--?*=*) options+=("${1%%=*}" "${1#*=}") ;; | |
# add --endopts for -- | |
--) options+=(--endopts) ;; | |
# Otherwise, nothing special | |
*) options+=("${1}") ;; | |
esac | |
shift | |
done | |
set -- "${options[@]}" | |
unset options | |
process_user_options $@ | |
# Trap bad exits with your cleanup function | |
trap trapCleanup EXIT INT TERM | |
# Set IFS to preferred implementation | |
IFS=$' \n\t' | |
# Exit on error. Append '||true' when you run the script if you expect an error. | |
#set -o errexit | |
# Run in debug mode, if set | |
if ${debug}; then set -x ; fi | |
# Exit on empty variable | |
if ${strict}; then set -o nounset ; fi | |
# Bash will remember & return the highest exitcode in a chain of pipes. | |
# This way you can catch the error in case mysqldump fails in `mysqldump |gzip`, for example. | |
set -o pipefail | |
# Run your script | |
mainScript | |
# Exit cleanly | |
safeExit | |
# vim: tabstop=4 shiftwidth=4 expandtab |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment