Skip to content

Instantly share code, notes, and snippets.

@zerolagtime
Created September 23, 2021 01:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zerolagtime/a6d985fecae4b29e91d03d3b4dd20a85 to your computer and use it in GitHub Desktop.
Save zerolagtime/a6d985fecae4b29e91d03d3b4dd20a85 to your computer and use it in GitHub Desktop.
Bash skeleton - supports colored output for WARN and ERROR
$ ./script-template.sh --noisy
[[[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:200] command -v /tmp/script-template.sh
[[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:200] dirname /tmp/script-template.sh
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:200] cd /tmp
[[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:201] pwd
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:201] here=/tmp
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:202] log_debug 'Starting main function of /tmp/script-template.sh'
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:159] main(): :
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:203] main
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:24] main(): log_info 'our informational message'
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:157] main(): :
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:25] main(): log_info 'Our additional parameters to the program are: '
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:157] main(): :
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:26] main(): log_warn 'False alarm - WITH_OPT is set to default (use --with-parameter)'
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:155] main(): :
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:27] main(): subroutine1 some value 'with spaces'
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:32] main(): for val in "$@"
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:33] main(): log_debug 'passed a subroutine a parameter: "some"'
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:159] subroutine1(): :
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:32] main(): for val in "$@"
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:33] main(): log_debug 'passed a subroutine a parameter: "value"'
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:159] subroutine1(): :
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:32] main(): for val in "$@"
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:33] main(): log_debug 'passed a subroutine a parameter: "with spaces"'
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:159] subroutine1(): :
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:35] main(): log_info 'the subroutine works'
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:157] subroutine1(): :
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:36] main(): return 0
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:28] main(): random_failure
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:39] main(): log_info 'This function will randomly fail and should result in the program exiting'
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:157] random_failure(): :
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:40] main(): '[' 0 == 0 ']'
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:41] main(): log_error 'Random failure'
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:153] random_failure(): :
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:42] main(): return 4
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:28] main(): return 4
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:204] err=4
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:205] '[' 4 -ne 0 ']'
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:206] log_error 'Completed main function of /tmp/script-template.sh with errors. Exiting with error code 4.'
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:153] main(): :
[Wed 22 Sep 2021 09:32:21 PM EDT][/tmp/script-template.sh:210] exit 4
$ ./script-template.sh
INFO: our informational message
INFO: Our additional parameters to the program are:
WARN: False alarm - WITH_OPT is set to default (use --with-parameter)
INFO: the subroutine works
INFO: This function will randomly fail and should result in the program exiting
INFO: Whew! We got away.
$ ./script-template.sh -v | more
[Thu Mar 18 17:01:12 EDT 2021][main] DEBUG: Starting main function of ./script-template.sh
[Thu Mar 18 17:01:12 EDT 2021][main] INFO: our informational message
[Thu Mar 18 17:01:12 EDT 2021][main] INFO: Our additional parameters to the program are:
[Thu Mar 18 17:01:12 EDT 2021][main] WARN: False alarm - WITH_OPT is set to default (use --with-parameter)
[Thu Mar 18 17:01:12 EDT 2021][subroutine1] DEBUG: passed a subroutine a parameter: "some"
[Thu Mar 18 17:01:12 EDT 2021][subroutine1] DEBUG: passed a subroutine a parameter: "value"
[Thu Mar 18 17:01:12 EDT 2021][subroutine1] DEBUG: passed a subroutine a parameter: "with spaces"
[Thu Mar 18 17:01:12 EDT 2021][subroutine1] INFO: the subroutine works
[Thu Mar 18 17:01:12 EDT 2021][random_failure] INFO: This function will randomly fail and should result in the program exiting
[Thu Mar 18 17:01:12 EDT 2021][random_failure] ERROR: Random failure
[Thu Mar 18 17:01:12 EDT 2021][main] ERROR: Completed main function of ./script-template.sh with errors. Exiting with error code 4.
$ ./script-template.sh -v | more
[Thu Mar 18 17:01:09 EDT 2021][main] DEBUG: Starting main function of ./script-template.sh
[Thu Mar 18 17:01:09 EDT 2021][main] INFO: our informational message
[Thu Mar 18 17:01:09 EDT 2021][main] INFO: Our additional parameters to the program are:
[Thu Mar 18 17:01:09 EDT 2021][main] WARN: False alarm - WITH_OPT is set to default (use --with-parameter)
[Thu Mar 18 17:01:09 EDT 2021][subroutine1] DEBUG: passed a subroutine a parameter: "some"
[Thu Mar 18 17:01:09 EDT 2021][subroutine1] DEBUG: passed a subroutine a parameter: "value"
[Thu Mar 18 17:01:09 EDT 2021][subroutine1] DEBUG: passed a subroutine a parameter: "with spaces"
[Thu Mar 18 17:01:09 EDT 2021][subroutine1] INFO: the subroutine works
[Thu Mar 18 17:01:09 EDT 2021][random_failure] INFO: This function will randomly fail and should result in the program exiting
[Thu Mar 18 17:01:09 EDT 2021][random_failure] INFO: Whew! We got away.
[Thu Mar 18 17:01:09 EDT 2021][main] DEBUG: Completed main function of ./script-template.sh. Exiting with error code 0.
#!/bin/bash
# This is a template script that has built-in logging
# with colored messages. It also supports command line parsing.
usage() {
echo "$0 [-h | --help] [-v | --verbose]"
echo " [-q | --quiet] [-w str | --with-parameter=str]"
echo " [--noisy]"
echo "Here is the reason this script exists."
echo "Where:"
echo "--with-parameter is an option that has a parameter"
echo "--help will display this help message"
echo "--verbose will show all debug messages"
echo "--quiet will only show warning or error messages"
echo "--noisy gives extremely detailed debug logs with"
echo " every command shown"
exit 1
}
main() {
# This is your main function. Command line parameters
# that can be parsed have been. Remaining parameters are in $*
# Return the exit code of your choice
log_info "our informational message"
log_info "Our additional parameters to the program are: $@"
log_warn "False alarm - WITH_OPT is set to $WITH_OPT (use --with-parameter)"
subroutine1 some value "with spaces" || return 1
random_failure || return $?
return 0
}
subroutine1() {
for val in "$@"; do
log_debug "passed a subroutine a parameter: \"$val\""
done
log_info "the subroutine works"
return 0
}
random_failure() {
log_info "This function will randomly fail and should result in the program exiting"
if [ $(($RANDOM % 2)) == 0 ]; then
log_error "Random failure"
return 4
else
log_info "Whew! We got away."
fi
return 0
}
#####################################################################
# No program code below this line
# Only set up command line options and environment variable defaults
LOG_LEVEL=info
declare -A LEVELS=( [error]=0 [warn]=1 [info]=2 [debug]=3 [noisy]=4)
if [ -t 1 ]; then # -t is "am stdout a terminal?"
ESC=$(printf "\E")
RED="${ESC}[31m"
YELLOW="${ESC}[33m"
CYAN="${ESC}[36m"
GREEN="${ESC}[32m"
RESET="${ESC}[0m"
else
ESC=''
RED=''
YELLOW=''
CYAN=''
GREEN=''
RESET=''
fi
__log() {
level=$1
if [ ${LEVELS[$level]} -gt ${LEVELS[$LOG_LEVEL]} ]; then
# the log level is to restrictive, so don't show
return
fi
shift
msg="$*"
preamble=""
if [ "$LOG_LEVEL" == "debug" ]; then
preamble="[$(date)][${FUNCNAME[2]}] "
fi
case $level in
error) echo "$preamble${RED}ERROR${RESET}: $msg" 1>&2 ;;
warn) echo "$preamble${YELLOW}WARN${RESET}: $msg" 1>&2 ;;
info) echo "$preamble${CYAN}INFO${RESET}: $msg" ;;
debug) echo "$preamble${GREEN}DEBUG${RESET}: $msg" ;;
esac
}
log_debug() {
if [ $# -gt 0 ]; then
__log debug "$@"
else
while read line; do __log debug "$line"; done
fi
}
log_info() {
if [ $# -gt 0 ]; then
__log info "$@"
else
while read line; do __log info "$line"; done
fi
}
log_warn() {
if [ $# -gt 0 ]; then
__log warn "$@"
else
while read line; do __log warn "$line"; done
fi
}
log_error() {
if [ $# -gt 0 ]; then
__log error "$@"
else
while read line; do __log error "$line"; done
fi
}
SHORT=hvw:q
LONG=help,verbose,with-parameter:,quiet,vv,very-verbose,noisy
OPTS=$(getopt --options $SHORT --long $LONG --name "$0" -- "$@")
[ $? -eq 0 ] || {
echo "Incorrect options provided"
usage
exit 1
}
cd "$(dirname $0)"
# initialize any defaults here
WITH_OPT=default
eval set -- "$OPTS"
while true; do
case "$1" in
-w | --with-parameter)
# sanitize WITH_OPT
WITH_OPT="${2//[;\/\\|]/}"
shift 2
;;
-v | --verbose)
LOG_LEVEL=debug
shift
;;
-q | --quiet)
LOG_LEVEL=warn
shift
;;
-h | --help)
usage
shift
;;
--vv | --very-verbose | --noisy)
LOG_LEVEL=noisy
# inspiration from https://wiki.bash-hackers.org/scripting/debuggingtips
log_error() { :
}
log_warn() { :
}
log_info() { :
}
log_debug() { :
}
__log() { :
};
export PS4='[$(date)][${BASH_SOURCE}:${LINENO}] ${FUNCNAME[1]:+${FUNCNAME[1]}(): }'
shift
;;
--)
shift
break
;;
esac
done
# Allow strings to have exclamation points in them
set +o histexpand
if [ "$LOG_LEVEL" == "noisy" ]; then
# note that PS4 is set to do all of the dumping
set -o xtrace
fi
# Uncomment this section if the user was expected to run this as root
#
# verify any command line parameters here
#if [ $UID != "0" ]; then
# log_error "This command should be run as root. Rerunning with sudo."
# sudo bash -c "$@"
# exit $?
#fi
# Uncomment this section and document the essential commands if
# this script will be broken without access to these tools.
# Include commands outside of a typical busybox minimalist system.
#
#log_debug "Ensuring that essential commands are in the PATH"
#for essential_command in ansible ssh; do
# if [ -z "$(command -v $essential_command)" ]; then
# log_error "An essential command is missing: $essential_command"
# exit 2
# fi
#done
cd $(dirname $(command -v $0))
here=$(pwd)
log_debug "Starting main function of $0"
main "$@"
err=$?
if [ $err -ne 0 ]; then
log_error "Completed main function of $0 with errors. Exiting with error code $err."
else
log_debug "Completed main function of $0. Exiting with error code $err."
fi
exit $err
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment