mirror of
https://github.com/natelandau/shell-scripting-templates.git
synced 2025-11-12 23:13:48 -05:00
Squashed commit of the following:
commit 61bf734812cb62ba6e0ec224bc15f7928705a8a2 Author: Nathaniel Landau <nate@natelandau.com> Date: Thu Oct 21 15:44:21 2021 -0400 Major overhaul continued - rename templates - add checks utilities - add new array utilities - rename files - add assorted utilities - improve documentation commit 546178fff3b526f492eb0eeffc63f79537e75de3 Author: Nathaniel Landau <nate@natelandau.com> Date: Wed Oct 20 16:31:14 2021 -0400 Update conventions commit f6d0642f85518efda9c5d8472b99d1c14163e381 Author: Nathaniel Landau <nate@natelandau.com> Date: Wed Oct 20 09:47:09 2021 -0400 minor formatting changes commit 2217612b55e3f9faf803a2d0c937ea2261206505 Author: Nathaniel Landau <nate@natelandau.com> Date: Tue Oct 19 17:59:09 2021 -0400 add new functions commit 347ba7aa738dcd6a5ad9d70886b38da3a17dc89e Author: Nathaniel Landau <nate@natelandau.com> Date: Tue Oct 19 12:06:44 2021 -0400 major overhaul - Add standaloneTemplate.sh - Rework README - Refactor inline documentation - Enforce coding standards - Remove CSV utilities - Add new array utilities - add _useGNUutils_ - more ... commit cd8e0d49aef25eeaf6b3e71a3c9e1f29ab9b06f5 Author: Nathaniel Landau <nate@natelandau.com> Date: Sun Oct 17 09:56:08 2021 -0400 Add debug functions commit f7c5c0a3d19815dcc6ba80b5f5a2ebb77ef88b07 Author: Nathaniel Landau <nate@natelandau.com> Date: Sat Oct 16 21:10:01 2021 -0400 add new array functions _joinArray_, _isEmptyArray_, _sortArray_, _reverseSortArray_, and _mergearrays_ commit d8bc3d8cabdbcee3c479f97b43a45bdfe3bdafe0 Author: Nathaniel Landau <nate@natelandau.com> Date: Fri Oct 15 17:27:12 2021 -0400 add _columnize_ commit 2fd2ae9435f476bc3968c3eb0d793db4bf1d9eaf Author: Nathaniel Landau <nate@natelandau.com> Date: Mon Oct 11 22:17:45 2021 -0400 _progressBar_: Fix unbound variable commit e8933d15fc955a1acc665e9a081f131e681855d5 Author: Nathaniel Landau <nate@natelandau.com> Date: Sun Oct 10 11:50:42 2021 -0400 _alert_: header now underlined commit c9ce894361dec7d3513c038794a155519baf26bc Author: Nathaniel Landau <nate@natelandau.com> Date: Tue Oct 5 09:49:42 2021 -0400 _alert_: line numbers to gray commit 4aaddd336ce613f629a7e6a62ef3b27ffc24d22d Author: Nathaniel Landau <nate@natelandau.com> Date: Fri Oct 8 15:05:20 2021 -0400 _usage_ to stdout commit e2372fc3122ec1f20acc27f04d29b3785f014e25 Author: Nathaniel Landau <nate@natelandau.com> Date: Tue Oct 5 09:38:26 2021 -0400 _setPATH_: remove unneeded logic commit e60c75b6c954ac4bd146e2758252168027b9a43d Author: Nathaniel Landau <nate@natelandau.com> Date: Tue Oct 5 09:25:38 2021 -0400 _findSource_: bugfix commit 0e84912e1ccd7203e5beff9f8737f8374f4aa5d8 Author: Nathaniel Landau <nate@natelandau.com> Date: Thu Sep 30 16:29:25 2021 -0400 add requirements to documentation commit 2c24843e3ada591e1868a94416e40b5ac0aa4994 Author: Nathaniel Landau <nate@natelandau.com> Date: Thu Sep 30 15:34:10 2021 -0400 _uniqueFilename_: improve extension handling commit 08bc2dfdcc8632efee9179e9c960a574fc17cf0c Author: Nathaniel Landau <nate@natelandau.com> Date: Mon Sep 27 15:13:53 2021 -0400 improve hooks script commit 641918f1559d3b3aa38a9bbdf418938b2b81c176 Author: Nathaniel Landau <nate@natelandau.com> Date: Fri Sep 24 08:16:52 2021 -0400 _inArry_: case insensitivity commit eae10f170680540fdb4a1222add7e54f8785ea63 Author: Nathaniel Landau <nate@natelandau.com> Date: Mon Sep 20 18:31:44 2021 -0400 clean up alerting commit 700acd56f57fd57db84ef0e232ef41cdd7aee43c Author: Nathaniel Landau <nate@natelandau.com> Date: Mon Sep 20 18:22:11 2021 -0400 refactor _execute_ commit d893f86900a9fed9d91a0c9cc06c13b6b34d9926 Author: Nathaniel Landau <nate@natelandau.com> Date: Mon Sep 20 18:19:18 2021 -0400 'fatal' replaces 'die' commit 3326857bf127bef36cd9982246aa5b826d796d0a Author: Nathaniel Landau <nate@natelandau.com> Date: Fri Sep 17 08:29:50 2021 -0400 _execute_: ensure quiet and verbose work together
This commit is contained in:
482
utilities/misc.bash
Normal file
482
utilities/misc.bash
Normal file
@@ -0,0 +1,482 @@
|
||||
# Functions which provide base functionality for other scripts
|
||||
|
||||
_checkTerminalSize_() {
|
||||
# DESC:
|
||||
# Checks the size of the terminal window. Updates LINES/COLUMNS if necessary
|
||||
# ARGS:
|
||||
# NONE
|
||||
# OUTS:
|
||||
# NONE
|
||||
# USAGE:
|
||||
# _updateTerminalSize_
|
||||
# CREDIT:
|
||||
# https://github.com/labbots/bash-utility
|
||||
|
||||
shopt -s checkwinsize && (: && :)
|
||||
trap 'shopt -s checkwinsize; (:;:)' SIGWINCH
|
||||
}
|
||||
|
||||
_detectOS_() {
|
||||
# DESC:
|
||||
# Identify the OS the script is run on
|
||||
# ARGS:
|
||||
# None
|
||||
# OUTS:
|
||||
# 0 - Success
|
||||
# 1 - Failed to detect OS
|
||||
# stdout: One of 'mac', 'linux', 'windows'
|
||||
# USAGE:
|
||||
# _detectOS_
|
||||
# CREDIT:
|
||||
# https://github.com/labbots/bash-utility
|
||||
|
||||
local _uname
|
||||
local _os
|
||||
if _uname=$(command -v uname); then
|
||||
case $("${_uname}" | tr '[:upper:]' '[:lower:]') in
|
||||
linux*)
|
||||
_os="linux"
|
||||
;;
|
||||
darwin*)
|
||||
_os="mac"
|
||||
;;
|
||||
msys* | cygwin* | mingw* | nt | win*)
|
||||
# or possible 'bash on windows'
|
||||
_os="windows"
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
printf "%s" "${_os}"
|
||||
|
||||
}
|
||||
|
||||
_detectLinuxDistro_() {
|
||||
# DESC:
|
||||
# Detects the Linux distribution of the host the script is run on
|
||||
# ARGS:
|
||||
# None
|
||||
# OUTS:
|
||||
# 0 - If Linux distro is successfully detected
|
||||
# 1 - If unable to detect OS distro or not on Linux
|
||||
# stdout: Prints name of Linux distro in lower case (ex: 'raspbian' or 'debian')
|
||||
# USAGE:
|
||||
# _detectLinuxDistro_
|
||||
# CREDIT:
|
||||
# https://github.com/labbots/bash-utility
|
||||
|
||||
local _distro
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
. "/etc/os-release"
|
||||
_distro="${NAME}"
|
||||
elif type lsb_release >/dev/null 2>&1; then
|
||||
# linuxbase.org
|
||||
_distro=$(lsb_release -si)
|
||||
elif [[ -f /etc/lsb-release ]]; then
|
||||
# For some versions of Debian/Ubuntu without lsb_release command
|
||||
# shellcheck disable=SC1091
|
||||
. /etc/lsb-release
|
||||
_distro="${DISTRIB_ID}"
|
||||
elif [[ -f /etc/debian_version ]]; then
|
||||
# Older Debian/Ubuntu/etc.
|
||||
_distro="debian"
|
||||
elif [[ -f /etc/SuSe-release ]]; then
|
||||
# Older SuSE/etc.
|
||||
_distro="suse"
|
||||
elif [[ -f /etc/redhat-release ]]; then
|
||||
# Older Red Hat, CentOS, etc.
|
||||
_distro="redhat"
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
printf "%s" "${_distro}" | tr '[:upper:]' '[:lower:]'
|
||||
}
|
||||
|
||||
_detectMacOSVersion_() {
|
||||
# DESC:
|
||||
# Detects the host's version of MacOS
|
||||
# ARGS:
|
||||
# None
|
||||
# OUTS:
|
||||
# 0 - Success
|
||||
# 1 - Can not find macOS version number or not on a mac
|
||||
# stdout: Prints the version number of macOS (ex: 11.6.1)
|
||||
# USAGE:
|
||||
# _detectMacOSVersion_
|
||||
# CREDIT:
|
||||
# https://github.com/labbots/bash-utility
|
||||
|
||||
[ ! "$(declare -f "_detectOS_")" ] && fatal "${FUNCNAME[0]} needs function _detectOS_"
|
||||
|
||||
if [[ "$(_detectOS_)" == "mac" ]]; then
|
||||
local _mac_version
|
||||
_mac_version="$(sw_vers -productVersion)"
|
||||
printf "%s" "${_mac_version}"
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
_detectLinuxDistro_() {
|
||||
# DESC:
|
||||
# Detects the Linux distribution of the host the script is run on
|
||||
# ARGS:
|
||||
# None
|
||||
# OUTS:
|
||||
# 0 - If Linux distro is successfully detected
|
||||
# 1 - If unable to detect OS distro or not on Linux
|
||||
# stdout: Prints name of Linux distro in lower case (ex: 'raspbian' or 'debian')
|
||||
# USAGE:
|
||||
# _detectLinuxDistro_
|
||||
# CREDIT:
|
||||
# https://github.com/labbots/bash-utility
|
||||
|
||||
local _distro
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
. "/etc/os-release"
|
||||
_distro="${NAME}"
|
||||
elif type lsb_release >/dev/null 2>&1; then
|
||||
# linuxbase.org
|
||||
_distro=$(lsb_release -si)
|
||||
elif [[ -f /etc/lsb-release ]]; then
|
||||
# For some versions of Debian/Ubuntu without lsb_release command
|
||||
# shellcheck disable=SC1091
|
||||
. /etc/lsb-release
|
||||
_distro="${DISTRIB_ID}"
|
||||
elif [[ -f /etc/debian_version ]]; then
|
||||
# Older Debian/Ubuntu/etc.
|
||||
_distro="debian"
|
||||
elif [[ -f /etc/SuSe-release ]]; then
|
||||
# Older SuSE/etc.
|
||||
_distro="suse"
|
||||
elif [[ -f /etc/redhat-release ]]; then
|
||||
# Older Red Hat, CentOS, etc.
|
||||
_distro="redhat"
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
printf "%s" "${_distro}" | tr '[:upper:]' '[:lower:]'
|
||||
}
|
||||
|
||||
_execute_() {
|
||||
# DESC:
|
||||
# Executes commands while respecting global DRYRUN, VERBOSE, LOGGING, and QUIET flags
|
||||
# ARGS:
|
||||
# $1 (Required) - The command to be executed. Quotation marks MUST be escaped.
|
||||
# $2 (Optional) - String to display after command is executed
|
||||
# OPTS:
|
||||
# -v Always print output from the execute function to STDOUT
|
||||
# -n Use NOTICE level alerting (default is INFO)
|
||||
# -p Pass a failed command with 'return 0'. This effectively bypasses set -e.
|
||||
# -e Bypass _alert_ functions and use 'echo RESULT'
|
||||
# -s Use '_alert_ success' for successful output. (default is 'info')
|
||||
# -q Do not print output (QUIET mode)
|
||||
# OUTS:
|
||||
# stdout: Configurable output
|
||||
# USE :
|
||||
# _execute_ "cp -R \"~/dir/somefile.txt\" \"someNewFile.txt\"" "Optional message"
|
||||
# _execute_ -sv "mkdir \"some/dir\""
|
||||
# NOTE:
|
||||
# If $DRYRUN=true, no commands are executed and the command that would have been executed
|
||||
# is printed to STDOUT using dryrun level alerting
|
||||
# If $VERBOSE=true, the command's native output is printed to stdout. This can be forced
|
||||
# with '_execute_ -v'
|
||||
|
||||
local _localVerbose=false
|
||||
local _passFailures=false
|
||||
local _echoResult=false
|
||||
local _echoSuccessResult=false
|
||||
local _quietMode=false
|
||||
local _echoNoticeResult=false
|
||||
local opt
|
||||
|
||||
local OPTIND=1
|
||||
while getopts ":vVpPeEsSqQnN" opt; do
|
||||
case $opt in
|
||||
v | V) _localVerbose=true ;;
|
||||
p | P) _passFailures=true ;;
|
||||
e | E) _echoResult=true ;;
|
||||
s | S) _echoSuccessResult=true ;;
|
||||
q | Q) _quietMode=true ;;
|
||||
n | N) _echoNoticeResult=true ;;
|
||||
*)
|
||||
{
|
||||
error "Unrecognized option '$1' passed to _execute_. Exiting."
|
||||
_safeExit_
|
||||
}
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND - 1))
|
||||
|
||||
[[ $# == 0 ]] && fatal "Missing required argument to ${FUNCNAME[0]}"
|
||||
|
||||
local _command="${1}"
|
||||
local _executeMessage="${2:-$1}"
|
||||
|
||||
local _saveVerbose=${VERBOSE}
|
||||
if "${_localVerbose}"; then
|
||||
VERBOSE=true
|
||||
fi
|
||||
|
||||
if "${DRYRUN}"; then
|
||||
if "${_quietMode}"; then
|
||||
VERBOSE=${_saveVerbose}
|
||||
return 0
|
||||
fi
|
||||
if [ -n "${2:-}" ]; then
|
||||
dryrun "${1} (${2})" "$(caller)"
|
||||
else
|
||||
dryrun "${1}" "$(caller)"
|
||||
fi
|
||||
elif ${VERBOSE}; then
|
||||
if eval "${_command}"; then
|
||||
if "${_quietMode}"; then
|
||||
VERBOSE=${_saveVerbose}
|
||||
elif "${_echoResult}"; then
|
||||
printf "%s\n" "${_executeMessage}"
|
||||
elif "${_echoSuccessResult}"; then
|
||||
success "${_executeMessage}"
|
||||
elif "${_echoNoticeResult}"; then
|
||||
notice "${_executeMessage}"
|
||||
else
|
||||
info "${_executeMessage}"
|
||||
fi
|
||||
else
|
||||
if "${_quietMode}"; then
|
||||
VERBOSE=${_saveVerbose}
|
||||
elif "${_echoResult}"; then
|
||||
printf "%s\n" "warning: ${_executeMessage}"
|
||||
else
|
||||
warning "${_executeMessage}"
|
||||
fi
|
||||
VERBOSE=${_saveVerbose}
|
||||
"${_passFailures}" && return 0 || return 1
|
||||
fi
|
||||
else
|
||||
if eval "${_command}" >/dev/null 2>&1; then
|
||||
if "${_quietMode}"; then
|
||||
VERBOSE=${_saveVerbose}
|
||||
elif "${_echoResult}"; then
|
||||
printf "%s\n" "${_executeMessage}"
|
||||
elif "${_echoSuccessResult}"; then
|
||||
success "${_executeMessage}"
|
||||
elif "${_echoNoticeResult}"; then
|
||||
notice "${_executeMessage}"
|
||||
else
|
||||
info "${_executeMessage}"
|
||||
fi
|
||||
else
|
||||
if "${_quietMode}"; then
|
||||
VERBOSE=$_saveVerbose
|
||||
elif "${_echoResult}"; then
|
||||
printf "%s\n" "error: ${_executeMessage}"
|
||||
else
|
||||
warning "${_executeMessage}"
|
||||
fi
|
||||
VERBOSE=${_saveVerbose}
|
||||
"${_passFailures}" && return 0 || return 1
|
||||
fi
|
||||
fi
|
||||
VERBOSE=${_saveVerbose}
|
||||
return 0
|
||||
}
|
||||
|
||||
_findBaseDir_() {
|
||||
# DESC:
|
||||
# Locates the real directory of the script being run. Similar to GNU readlink -n
|
||||
# ARGS:
|
||||
# None
|
||||
# OUTS:
|
||||
# stdout: prints result
|
||||
# USAGE:
|
||||
# baseDir="$(_findBaseDir_)"
|
||||
# cp "$(_findBaseDir_ "somefile.txt")" "other_file.txt"
|
||||
|
||||
local _source
|
||||
local _dir
|
||||
|
||||
# Is file sourced?
|
||||
[[ $_ != "$0" ]] \
|
||||
&& _source="${BASH_SOURCE[1]}" \
|
||||
|| _source="${BASH_SOURCE[0]}"
|
||||
|
||||
while [ -h "${_source}" ]; do # Resolve $SOURCE until the file is no longer a symlink
|
||||
_dir="$(cd -P "$(dirname "${_source}")" && pwd)"
|
||||
_source="$(readlink "${_source}")"
|
||||
[[ ${_source} != /* ]] && _source="${_dir}/${_source}" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
|
||||
done
|
||||
printf "%s/n" "$(cd -P "$(dirname "${_source}")" && pwd)"
|
||||
}
|
||||
|
||||
_generateUUID_() {
|
||||
# DESC:
|
||||
# Generates a UUID
|
||||
# ARGS:
|
||||
# None
|
||||
# OUTS:
|
||||
# 0 - Success
|
||||
# 1 - Failure
|
||||
# stdout: UUID
|
||||
# USAGE:
|
||||
# _generateUUID_
|
||||
# CREDIT:
|
||||
# https://github.com/labbots/bash-utility
|
||||
|
||||
local _c
|
||||
local n
|
||||
local _b
|
||||
_c="89ab"
|
||||
|
||||
for ((n = 0; n < 16; ++n)); do
|
||||
_b="$((RANDOM % 256))"
|
||||
|
||||
case "$n" in
|
||||
6) printf '4%x' "$((_b % 16))" ;;
|
||||
8) printf '%c%x' "${_c:$RANDOM%${#_c}:1}" "$((_b % 16))" ;;
|
||||
|
||||
3 | 5 | 7 | 9)
|
||||
printf '%02x-' "${_b}"
|
||||
;;
|
||||
|
||||
*)
|
||||
printf '%02x' "${_b}"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
printf '\n'
|
||||
}
|
||||
|
||||
_makeProgressBar_() {
|
||||
# DESC:
|
||||
# Prints a progress bar within a for/while loop
|
||||
# ARGS:
|
||||
# $1 (Required) - The total number of items counted
|
||||
# $2 (Optional) - The optional title of the progress bar
|
||||
# OUTS: stdout: progress bar
|
||||
# USAGE:
|
||||
# for number in $(seq 0 100); do
|
||||
# sleep 1
|
||||
# _makeProgressBar_ "100" "Counting numbers"
|
||||
# done
|
||||
|
||||
[[ $# == 0 ]] && return # Do nothing if no arguments are passed
|
||||
(${QUIET}) && return
|
||||
(${VERBOSE}) && return
|
||||
[ ! -t 1 ] && return # Do nothing if the output is not a terminal
|
||||
[ ${1} == 1 ] && return # Do nothing with a single element
|
||||
|
||||
local n="${1}"
|
||||
local _width=30
|
||||
local _barCharacter="#"
|
||||
local _percentage
|
||||
local _num
|
||||
local _bar
|
||||
local _progressBarLine
|
||||
local _barTitle="${2:-Running Process}"
|
||||
local n
|
||||
|
||||
((n = n - 1))
|
||||
|
||||
# Reset the count
|
||||
[ -z "${progressBarProgress:-}" ] && progressBarProgress=0
|
||||
tput civis # Hide the cursor
|
||||
trap 'tput cnorm; exit 1' SIGINT
|
||||
|
||||
if [ ! "${progressBarProgress}" -eq $n ]; then
|
||||
#echo "progressBarProgress: $progressBarProgress"
|
||||
# Compute the percentage.
|
||||
_percentage=$((progressBarProgress * 100 / $1))
|
||||
# Compute the number of blocks to represent the percentage.
|
||||
_num=$((progressBarProgress * _width / $1))
|
||||
# Create the progress bar string.
|
||||
_bar=""
|
||||
if [ ${_num} -gt 0 ]; then
|
||||
_bar=$(printf "%0.s${_barCharacter}" $(seq 1 ${_num}))
|
||||
fi
|
||||
# Print the progress bar.
|
||||
_progressBarLine=$(printf "%s [%-${_width}s] (%d%%)" " ${_barTitle}" "${_bar}" "${_percentage}")
|
||||
printf "%s\r" "${_progressBarLine}"
|
||||
# echo -ne "${_progressBarLine}\r"
|
||||
progressBarProgress=$((progressBarProgress + 1))
|
||||
else
|
||||
# Clear the progress bar when complete
|
||||
# echo -ne "\033[0K\r"
|
||||
tput el # Clear the line
|
||||
|
||||
unset progressBarProgress
|
||||
fi
|
||||
|
||||
tput cnorm
|
||||
}
|
||||
|
||||
_runAsRoot_() {
|
||||
# DESC:
|
||||
# Run the requested command as root (via sudo if requested)
|
||||
# ARGS:
|
||||
# $1 (optional): Set to zero to not attempt execution via sudo
|
||||
# $@ (required): Passed through for execution as root user
|
||||
# OUTS:
|
||||
# Runs the requested command as root
|
||||
# CREDIT:
|
||||
# https://github.com/ralish/bash-script-template
|
||||
|
||||
[[ $# == 0 ]] && fatal "Missing required argument to ${FUNCNAME[0]}"
|
||||
|
||||
local _skip_sudo=false
|
||||
|
||||
if [[ ${1} =~ ^0$ ]]; then
|
||||
_skip_sudo=true
|
||||
shift
|
||||
fi
|
||||
|
||||
if [[ ${EUID} -eq 0 ]]; then
|
||||
"$@"
|
||||
elif [[ -z ${_skip_sudo} ]]; then
|
||||
sudo -H -- "$@"
|
||||
else
|
||||
fatal "Unable to run requested command as root: $*"
|
||||
fi
|
||||
}
|
||||
|
||||
_seekConfirmation_() {
|
||||
# DESC:
|
||||
# Seek user input for yes/no question
|
||||
# ARGS:
|
||||
# $1 (Required) - Question being asked
|
||||
# OUTS:
|
||||
# 0 if answer is "yes"
|
||||
# 1 if answer is "no"
|
||||
# USAGE:
|
||||
# _seekConfirmation_ "Do something?" && echo "okay" || echo "not okay"
|
||||
# OR
|
||||
# if _seekConfirmation_ "Answer this question"; then
|
||||
# something
|
||||
# fi
|
||||
|
||||
[[ $# == 0 ]] && fatal "Missing required argument to ${FUNCNAME[0]}"
|
||||
|
||||
local _yesNo
|
||||
input "${1}"
|
||||
if "${FORCE}"; then
|
||||
debug "Forcing confirmation with '--force' flag set"
|
||||
echo -e ""
|
||||
return 0
|
||||
else
|
||||
while true; do
|
||||
read -r -p " (y/n) " _yesNo
|
||||
case ${_yesNo} in
|
||||
[Yy]*) return 0 ;;
|
||||
[Nn]*) return 1 ;;
|
||||
*) input "Please answer yes or no." ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
}
|
||||
Reference in New Issue
Block a user