mirror of
https://github.com/natelandau/shell-scripting-templates.git
synced 2025-11-08 13:13:47 -05:00
368 lines
12 KiB
Bash
368 lines
12 KiB
Bash
# Functions for providing alerts to the user and logging them
|
|
# shellcheck disable=SC2034,SC2154
|
|
|
|
_setColors_() {
|
|
# DESC:
|
|
# Sets colors use for alerts.
|
|
# ARGS:
|
|
# None
|
|
# OUTS:
|
|
# None
|
|
# USAGE:
|
|
# printf "%s\n" "${blue}Some text${reset}"
|
|
|
|
if tput setaf 1 >/dev/null 2>&1; then
|
|
bold=$(tput bold)
|
|
underline=$(tput smul)
|
|
reverse=$(tput rev)
|
|
reset=$(tput sgr0)
|
|
|
|
if [[ $(tput colors) -ge 256 ]] >/dev/null 2>&1; then
|
|
white=$(tput setaf 231)
|
|
blue=$(tput setaf 38)
|
|
yellow=$(tput setaf 11)
|
|
green=$(tput setaf 82)
|
|
red=$(tput setaf 9)
|
|
purple=$(tput setaf 171)
|
|
gray=$(tput setaf 250)
|
|
else
|
|
white=$(tput setaf 7)
|
|
blue=$(tput setaf 38)
|
|
yellow=$(tput setaf 3)
|
|
green=$(tput setaf 2)
|
|
red=$(tput setaf 9)
|
|
purple=$(tput setaf 13)
|
|
gray=$(tput setaf 7)
|
|
fi
|
|
else
|
|
bold="\033[4;37m"
|
|
reset="\033[0m"
|
|
underline="\033[4;37m"
|
|
# shellcheck disable=SC2034
|
|
reverse=""
|
|
white="\033[0;37m"
|
|
blue="\033[0;34m"
|
|
yellow="\033[0;33m"
|
|
green="\033[1;32m"
|
|
red="\033[0;31m"
|
|
purple="\033[0;35m"
|
|
gray="\033[0;37m"
|
|
fi
|
|
}
|
|
|
|
_alert_() {
|
|
# DESC:
|
|
# Controls all printing of messages to log files and stdout.
|
|
# ARGS:
|
|
# $1 (required) - The type of alert to print
|
|
# (success, header, notice, dryrun, debug, warning, error,
|
|
# fatal, info, input)
|
|
# $2 (required) - The message to be printed to stdout and/or a log file
|
|
# $3 (optional) - Pass '${LINENO}' to print the line number where the _alert_ was triggered
|
|
# OUTS:
|
|
# stdout: The message is printed to stdout
|
|
# log file: The message is printed to a log file
|
|
# USAGE:
|
|
# [_alertType] "[MESSAGE]" "${LINENO}"
|
|
# NOTES:
|
|
# - The colors of each alert type are set in this function
|
|
# - For specified alert types, the funcstac will be printed
|
|
|
|
local _color
|
|
local _alertType="${1}"
|
|
local _message="${2}"
|
|
local _line="${3:-}" # Optional line number
|
|
|
|
[[ $# -lt 2 ]] && fatal 'Missing required argument to _alert_'
|
|
|
|
if [[ -n ${_line} && ${_alertType} =~ ^(fatal|error) && ${FUNCNAME[2]} != "_trapCleanup_" ]]; then
|
|
_message="${_message} ${gray}(line: ${_line}) $(_printFuncStack_)"
|
|
elif [[ -n ${_line} && ${FUNCNAME[2]} != "_trapCleanup_" ]]; then
|
|
_message="${_message} ${gray}(line: ${_line})"
|
|
elif [[ -z ${_line} && ${_alertType} =~ ^(fatal|error) && ${FUNCNAME[2]} != "_trapCleanup_" ]]; then
|
|
_message="${_message} ${gray}$(_printFuncStack_)"
|
|
fi
|
|
|
|
if [[ ${_alertType} =~ ^(error|fatal) ]]; then
|
|
_color="${bold}${red}"
|
|
elif [ "${_alertType}" == "info" ]; then
|
|
_color="${gray}"
|
|
elif [ "${_alertType}" == "warning" ]; then
|
|
_color="${red}"
|
|
elif [ "${_alertType}" == "success" ]; then
|
|
_color="${green}"
|
|
elif [ "${_alertType}" == "debug" ]; then
|
|
_color="${purple}"
|
|
elif [ "${_alertType}" == "header" ]; then
|
|
_color="${bold}${white}${underline}"
|
|
elif [ "${_alertType}" == "notice" ]; then
|
|
_color="${bold}"
|
|
elif [ "${_alertType}" == "input" ]; then
|
|
_color="${bold}${underline}"
|
|
elif [ "${_alertType}" = "dryrun" ]; then
|
|
_color="${blue}"
|
|
else
|
|
_color=""
|
|
fi
|
|
|
|
_writeToScreen_() {
|
|
[[ ${QUIET} == true ]] && return 0 # Print to console when script is not 'quiet'
|
|
[[ ${VERBOSE} == false && ${_alertType} =~ ^(debug|verbose) ]] && return 0
|
|
|
|
if ! [[ -t 1 || -z ${TERM:-} ]]; then # Don't use colors on non-recognized terminals
|
|
_color=""
|
|
reset=""
|
|
fi
|
|
|
|
if [[ ${_alertType} == header ]]; then
|
|
printf "${_color}%s${reset}\n" "${_message}"
|
|
else
|
|
printf "${_color}[%7s] %s${reset}\n" "${_alertType}" "${_message}"
|
|
fi
|
|
}
|
|
_writeToScreen_
|
|
|
|
_writeToLog_() {
|
|
[[ ${_alertType} == "input" ]] && return 0
|
|
[[ ${LOGLEVEL} =~ (off|OFF|Off) ]] && return 0
|
|
if [ -z "${LOGFILE:-}" ]; then
|
|
LOGFILE="$(pwd)/$(basename "$0").log"
|
|
fi
|
|
[ ! -d "$(dirname "${LOGFILE}")" ] && mkdir -p "$(dirname "${LOGFILE}")"
|
|
[[ ! -f ${LOGFILE} ]] && touch "${LOGFILE}"
|
|
|
|
# Don't use colors in logs
|
|
local _cleanmessage
|
|
_cleanmessage="$(printf "%s" "${_message}" | sed -E 's/(\x1b)?\[(([0-9]{1,2})(;[0-9]{1,3}){0,2})?[mGK]//g')"
|
|
# Print message to log file
|
|
printf "%s [%7s] %s %s\n" "$(date +"%b %d %R:%S")" "${_alertType}" "[$(/bin/hostname)]" "${_cleanmessage}" >>"${LOGFILE}"
|
|
}
|
|
|
|
# Write specified log level data to logfile
|
|
case "${LOGLEVEL:-ERROR}" in
|
|
ALL | all | All)
|
|
_writeToLog_
|
|
;;
|
|
DEBUG | debug | Debug)
|
|
_writeToLog_
|
|
;;
|
|
INFO | info | Info)
|
|
if [[ ${_alertType} =~ ^(error|fatal|warning|info|notice|success) ]]; then
|
|
_writeToLog_
|
|
fi
|
|
;;
|
|
NOTICE | notice | Notice)
|
|
if [[ ${_alertType} =~ ^(error|fatal|warning|notice|success) ]]; then
|
|
_writeToLog_
|
|
fi
|
|
;;
|
|
WARN | warn | Warn)
|
|
if [[ ${_alertType} =~ ^(error|fatal|warning) ]]; then
|
|
_writeToLog_
|
|
fi
|
|
;;
|
|
ERROR | error | Error)
|
|
if [[ ${_alertType} =~ ^(error|fatal) ]]; then
|
|
_writeToLog_
|
|
fi
|
|
;;
|
|
FATAL | fatal | Fatal)
|
|
if [[ ${_alertType} =~ ^fatal ]]; then
|
|
_writeToLog_
|
|
fi
|
|
;;
|
|
OFF | off)
|
|
return 0
|
|
;;
|
|
*)
|
|
if [[ ${_alertType} =~ ^(error|fatal) ]]; then
|
|
_writeToLog_
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
} # /_alert_
|
|
|
|
error() { _alert_ error "${1}" "${2:-}"; }
|
|
warning() { _alert_ warning "${1}" "${2:-}"; }
|
|
notice() { _alert_ notice "${1}" "${2:-}"; }
|
|
info() { _alert_ info "${1}" "${2:-}"; }
|
|
success() { _alert_ success "${1}" "${2:-}"; }
|
|
dryrun() { _alert_ dryrun "${1}" "${2:-}"; }
|
|
input() { _alert_ input "${1}" "${2:-}"; }
|
|
header() { _alert_ header "${1}" "${2:-}"; }
|
|
debug() { _alert_ debug "${1}" "${2:-}"; }
|
|
fatal() {
|
|
_alert_ fatal "${1}" "${2:-}"
|
|
_safeExit_ "1"
|
|
}
|
|
|
|
_printFuncStack_() {
|
|
# DESC:
|
|
# Prints the function stack in use. Used for debugging, and error reporting.
|
|
# ARGS:
|
|
# None
|
|
# OUTS:
|
|
# stdout: Prints [function]:[file]:[line]
|
|
# NOTE:
|
|
# Does not print functions from the alert class
|
|
local _i
|
|
declare -a _funcStackResponse=()
|
|
for ((_i = 1; _i < ${#BASH_SOURCE[@]}; _i++)); do
|
|
case "${FUNCNAME[${_i}]}" in
|
|
_alert_ | _trapCleanup_ | fatal | error | warning | notice | info | debug | dryrun | header | success)
|
|
continue
|
|
;;
|
|
*)
|
|
_funcStackResponse+=("${FUNCNAME[${_i}]}:$(basename "${BASH_SOURCE[${_i}]}"):${BASH_LINENO[_i - 1]}")
|
|
;;
|
|
esac
|
|
|
|
done
|
|
printf "( "
|
|
printf %s "${_funcStackResponse[0]}"
|
|
printf ' < %s' "${_funcStackResponse[@]:1}"
|
|
printf ' )\n'
|
|
}
|
|
|
|
_centerOutput_() {
|
|
# DESC:
|
|
# Prints text centered in the terminal window with an optional fill character
|
|
# ARGS:
|
|
# $1 (required): Text to center
|
|
# $2 (optional): Fill character
|
|
# OUTS:
|
|
# 0 - Success
|
|
# 1 - Failure
|
|
# stdout:
|
|
# USAGE:
|
|
# _centerOutput_ "Text to print in the center" "-"
|
|
# CREDIT:
|
|
# https://github.com/labbots/bash-utility
|
|
|
|
[[ $# == 0 ]] && fatal "Missing required argument to ${FUNCNAME[0]}"
|
|
local _input="${1}"
|
|
local _symbol="${2:- }"
|
|
local _filler
|
|
local _out
|
|
local _no_ansi_out
|
|
local i
|
|
|
|
_no_ansi_out=$(_stripANSI_ "${_input}")
|
|
declare -i _str_len=${#_no_ansi_out}
|
|
declare -i _filler_len="$(((COLUMNS - _str_len) / 2))"
|
|
|
|
[[ -n ${_symbol} ]] && _symbol="${_symbol:0:1}"
|
|
for ((i = 0; i < _filler_len; i++)); do
|
|
_filler+="${_symbol}"
|
|
done
|
|
|
|
_out="${_filler}${_input}${_filler}"
|
|
[[ $(((COLUMNS - _str_len) % 2)) -ne 0 ]] && _out+="${_symbol}"
|
|
printf "%s\n" "${_out}"
|
|
}
|
|
|
|
_clearLine_() (
|
|
# DESC:
|
|
# Clears output in the terminal on the specified line number.
|
|
# ARGS:
|
|
# $1 (Optional): Line number to clear. (Defaults to 1)
|
|
# OUTS:
|
|
# 0: Success
|
|
# 1: Failure
|
|
# USAGE:
|
|
# _clearLine_ "2"
|
|
|
|
! declare -f _isTerminal_ &>/dev/null && fatal "${FUNCNAME[0]} needs function _isTerminal_"
|
|
|
|
local _num="${1:-1}"
|
|
local i
|
|
|
|
if _isTerminal_; then
|
|
for ((i = 0; i < _num; i++)); do
|
|
printf "\033[A\033[2K"
|
|
done
|
|
fi
|
|
)
|
|
|
|
_columns_() {
|
|
# DESC:
|
|
# Prints a two column output with fixed widths and wrapping text from a key/value pair.
|
|
# Optionally pass a number of 2 space tabs to indent the output.
|
|
# ARGS:
|
|
# $1 (required): Key name (Left column text)
|
|
# $2 (required): Long value (Right column text. Wraps around if too long)
|
|
# $3 (optional): Number of 2 character tabs to indent the command (default 1)
|
|
# $4 (optional): Total character width of the left column (default 35)
|
|
# OPTS:
|
|
# -b Bold the left column
|
|
# -u Underline the left column
|
|
# -r Reverse background and foreground colors
|
|
# OUTS:
|
|
# stdout: Prints the output in columns
|
|
# NOTE:
|
|
# Long text or ANSI colors in the first column may create display issues
|
|
# USAGE:
|
|
# _columns_ "Key" "Long value text" [tab level]
|
|
|
|
[[ $# -lt 2 ]] && fatal "Missing required argument to ${FUNCNAME[0]}"
|
|
|
|
local opt
|
|
local OPTIND=1
|
|
local _style=""
|
|
while getopts ":bBuUrR" opt; do
|
|
case ${opt} in
|
|
b | B) _style="${_style}${bold}" ;;
|
|
u | U) _style="${_style}${underline}" ;;
|
|
r | R) _style="${_style}${reverse}" ;;
|
|
*) fatal "Unrecognized option '${1}' passed to ${FUNCNAME[0]}. Exiting." ;;
|
|
esac
|
|
done
|
|
shift $((OPTIND - 1))
|
|
|
|
local _key="${1}"
|
|
local _value="${2}"
|
|
local _tabLevel="${3:-0}"
|
|
local _leftColumnWidth="${4:-35}"
|
|
local _tabSize=2
|
|
local _line
|
|
local _rightIndent
|
|
local _leftIndent
|
|
|
|
_leftIndent="$((_tabLevel * _tabSize))"
|
|
|
|
local _leftColumnWidth="$((_leftColumnWidth - _leftIndent))"
|
|
|
|
if [ "$(tput cols)" -gt 180 ]; then
|
|
_rightIndent=110
|
|
elif [ "$(tput cols)" -gt 160 ]; then
|
|
_rightIndent=90
|
|
elif [ "$(tput cols)" -gt 130 ]; then
|
|
_rightIndent=60
|
|
elif [ "$(tput cols)" -gt 120 ]; then
|
|
_rightIndent=50
|
|
elif [ "$(tput cols)" -gt 110 ]; then
|
|
_rightIndent=40
|
|
elif [ "$(tput cols)" -gt 100 ]; then
|
|
_rightIndent=30
|
|
elif [ "$(tput cols)" -gt 90 ]; then
|
|
_rightIndent=20
|
|
elif [ "$(tput cols)" -gt 80 ]; then
|
|
_rightIndent=10
|
|
else
|
|
_rightIndent=0
|
|
fi
|
|
|
|
local _rightWrapLength=$(($(tput cols) - _leftColumnWidth - _leftIndent - _rightIndent))
|
|
|
|
local _first_line=0
|
|
while read -r _line; do
|
|
if [[ ${_first_line} -eq 0 ]]; then
|
|
_first_line=1
|
|
else
|
|
_key=" "
|
|
fi
|
|
printf "%-${_leftIndent}s${_style}%-${_leftColumnWidth}b${reset} %b\n" "" "${_key}${reset}" "${_line}"
|
|
done <<<"$(fold -w${_rightWrapLength} -s <<<"${_value}")"
|
|
}
|