Browse Source
Because: - These are commonly used funcionalities inside bash scripts Usage: - This file sould be sourced in the script file Refrence: https://github.com/ralish/bash-script-templatepull/2/head
Ali Hatami Tajik
2 years ago
1 changed files with 394 additions and 0 deletions
@ -0,0 +1,394 @@ |
|||||
|
#!/usr/bin/env bash |
||||
|
|
||||
|
# DESC: Handler for unexpected errors |
||||
|
# ARGS: $1 (optional): Exit code (defaults to 1) |
||||
|
# OUTS: None |
||||
|
function script_trap_err() { |
||||
|
local exit_code=1 |
||||
|
|
||||
|
# Disable the error trap handler to prevent potential recursion |
||||
|
trap - ERR |
||||
|
|
||||
|
# Consider any further errors non-fatal to ensure we run to completion |
||||
|
set +o errexit |
||||
|
set +o pipefail |
||||
|
|
||||
|
# Validate any provided exit code |
||||
|
if [[ ${1-} =~ ^[0-9]+$ ]]; then |
||||
|
exit_code="$1" |
||||
|
fi |
||||
|
|
||||
|
# Output debug data if in Cron mode |
||||
|
if [[ -n ${cron-} ]]; then |
||||
|
# Restore original file output descriptors |
||||
|
if [[ -n ${script_output-} ]]; then |
||||
|
exec 1>&3 2>&4 |
||||
|
fi |
||||
|
|
||||
|
# Print basic debugging information |
||||
|
printf '%b\n' "$ta_none" |
||||
|
printf '***** Abnormal termination of script *****\n' |
||||
|
printf 'Script Path: %s\n' "$script_path" |
||||
|
printf 'Script Parameters: %s\n' "$script_params" |
||||
|
printf 'Script Exit Code: %s\n' "$exit_code" |
||||
|
|
||||
|
# Print the script log if we have it. It's possible we may not if we |
||||
|
# failed before we even called cron_init(). This can happen if bad |
||||
|
# parameters were passed to the script so we bailed out very early. |
||||
|
if [[ -n ${script_output-} ]]; then |
||||
|
# shellcheck disable=SC2312 |
||||
|
printf 'Script Output:\n\n%s' "$(cat "$script_output")" |
||||
|
else |
||||
|
printf 'Script Output: None (failed before log init)\n' |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
# Exit with failure status |
||||
|
exit "$exit_code" |
||||
|
} |
||||
|
|
||||
|
# DESC: Handler for exiting the script |
||||
|
# ARGS: None |
||||
|
# OUTS: None |
||||
|
function script_trap_exit() { |
||||
|
cd "$orig_cwd" |
||||
|
|
||||
|
# Remove Cron mode script log |
||||
|
if [[ -n ${cron-} && -f ${script_output-} ]]; then |
||||
|
rm "$script_output" |
||||
|
fi |
||||
|
|
||||
|
# Remove script execution lock |
||||
|
if [[ -d ${script_lock-} ]]; then |
||||
|
rmdir "$script_lock" |
||||
|
fi |
||||
|
|
||||
|
# Restore terminal colours |
||||
|
printf '%b' "$ta_none" |
||||
|
} |
||||
|
|
||||
|
# DESC: Exit script with the given message |
||||
|
# ARGS: $1 (required): Message to print on exit |
||||
|
# $2 (optional): Exit code (defaults to 0) |
||||
|
# OUTS: None |
||||
|
# NOTE: The convention used in this script for exit codes is: |
||||
|
# 0: Normal exit |
||||
|
# 1: Abnormal exit due to external error |
||||
|
# 2: Abnormal exit due to script error |
||||
|
function script_exit() { |
||||
|
if [[ $# -eq 1 ]]; then |
||||
|
printf '%s\n' "$1" |
||||
|
exit 0 |
||||
|
fi |
||||
|
|
||||
|
if [[ ${2-} =~ ^[0-9]+$ ]]; then |
||||
|
printf '%b\n' "$1" |
||||
|
# If we've been provided a non-zero exit code run the error trap |
||||
|
if [[ $2 -ne 0 ]]; then |
||||
|
script_trap_err "$2" |
||||
|
else |
||||
|
exit 0 |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
script_exit 'Missing required argument to script_exit()!' 2 |
||||
|
} |
||||
|
|
||||
|
# DESC: Generic script initialisation |
||||
|
# ARGS: $@ (optional): Arguments provided to the script |
||||
|
# OUTS: $orig_cwd: The current working directory when the script was run |
||||
|
# $script_path: The full path to the script |
||||
|
# $script_dir: The directory path of the script |
||||
|
# $script_name: The file name of the script |
||||
|
# $script_params: The original parameters provided to the script |
||||
|
# $ta_none: The ANSI control code to reset all text attributes |
||||
|
# NOTE: $script_path only contains the path that was used to call the script |
||||
|
# and will not resolve any symlinks which may be present in the path. |
||||
|
# You can use a tool like realpath to obtain the "true" path. The same |
||||
|
# caveat applies to both the $script_dir and $script_name variables. |
||||
|
# shellcheck disable=SC2034 |
||||
|
function script_init() { |
||||
|
# Useful variables |
||||
|
readonly orig_cwd="$PWD" |
||||
|
readonly script_params="$*" |
||||
|
readonly script_path="${BASH_SOURCE[1]}" |
||||
|
script_dir="$(dirname "$script_path")" |
||||
|
script_name="$(basename "$script_path")" |
||||
|
readonly script_dir script_name |
||||
|
|
||||
|
# Important to always set as we use it in the exit handler |
||||
|
# shellcheck disable=SC2155 |
||||
|
readonly ta_none="$(tput sgr0 2> /dev/null || true)" |
||||
|
} |
||||
|
|
||||
|
# DESC: Initialise colour variables |
||||
|
# ARGS: None |
||||
|
# OUTS: Read-only variables with ANSI control codes |
||||
|
# NOTE: If --no-colour was set the variables will be empty. The output of the |
||||
|
# $ta_none variable after each tput is redundant during normal execution, |
||||
|
# but ensures the terminal output isn't mangled when running with xtrace. |
||||
|
# shellcheck disable=SC2034,SC2155 |
||||
|
function colour_init() { |
||||
|
if [[ -z ${no_colour-} ]]; then |
||||
|
# Text attributes |
||||
|
readonly ta_bold="$(tput bold 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
readonly ta_uscore="$(tput smul 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
readonly ta_blink="$(tput blink 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
readonly ta_reverse="$(tput rev 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
readonly ta_conceal="$(tput invis 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
|
||||
|
# Foreground codes |
||||
|
readonly fg_black="$(tput setaf 0 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
readonly fg_blue="$(tput setaf 4 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
readonly fg_cyan="$(tput setaf 6 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
readonly fg_green="$(tput setaf 2 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
readonly fg_magenta="$(tput setaf 5 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
readonly fg_red="$(tput setaf 1 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
readonly fg_white="$(tput setaf 7 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
readonly fg_yellow="$(tput setaf 3 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
|
||||
|
# Background codes |
||||
|
readonly bg_black="$(tput setab 0 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
readonly bg_blue="$(tput setab 4 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
readonly bg_cyan="$(tput setab 6 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
readonly bg_green="$(tput setab 2 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
readonly bg_magenta="$(tput setab 5 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
readonly bg_red="$(tput setab 1 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
readonly bg_white="$(tput setab 7 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
readonly bg_yellow="$(tput setab 3 2> /dev/null || true)" |
||||
|
printf '%b' "$ta_none" |
||||
|
else |
||||
|
# Text attributes |
||||
|
readonly ta_bold='' |
||||
|
readonly ta_uscore='' |
||||
|
readonly ta_blink='' |
||||
|
readonly ta_reverse='' |
||||
|
readonly ta_conceal='' |
||||
|
|
||||
|
# Foreground codes |
||||
|
readonly fg_black='' |
||||
|
readonly fg_blue='' |
||||
|
readonly fg_cyan='' |
||||
|
readonly fg_green='' |
||||
|
readonly fg_magenta='' |
||||
|
readonly fg_red='' |
||||
|
readonly fg_white='' |
||||
|
readonly fg_yellow='' |
||||
|
|
||||
|
# Background codes |
||||
|
readonly bg_black='' |
||||
|
readonly bg_blue='' |
||||
|
readonly bg_cyan='' |
||||
|
readonly bg_green='' |
||||
|
readonly bg_magenta='' |
||||
|
readonly bg_red='' |
||||
|
readonly bg_white='' |
||||
|
readonly bg_yellow='' |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
# DESC: Initialise Cron mode |
||||
|
# ARGS: None |
||||
|
# OUTS: $script_output: Path to the file stdout & stderr was redirected to |
||||
|
function cron_init() { |
||||
|
if [[ -n ${cron-} ]]; then |
||||
|
# Redirect all output to a temporary file |
||||
|
script_output="$(mktemp --tmpdir "$script_name".XXXXX)" |
||||
|
readonly script_output |
||||
|
exec 3>&1 4>&2 1> "$script_output" 2>&1 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
# DESC: Acquire script lock |
||||
|
# ARGS: $1 (optional): Scope of script execution lock (system or user) |
||||
|
# OUTS: $script_lock: Path to the directory indicating we have the script lock |
||||
|
# NOTE: This lock implementation is extremely simple but should be reliable |
||||
|
# across all platforms. It does *not* support locking a script with |
||||
|
# symlinks or multiple hardlinks as there's no portable way of doing so. |
||||
|
# If the lock was acquired it's automatically released on script exit. |
||||
|
function lock_init() { |
||||
|
local lock_dir |
||||
|
if [[ $1 = 'system' ]]; then |
||||
|
lock_dir="/tmp/$script_name.lock" |
||||
|
elif [[ $1 = 'user' ]]; then |
||||
|
lock_dir="/tmp/$script_name.$UID.lock" |
||||
|
else |
||||
|
script_exit 'Missing or invalid argument to lock_init()!' 2 |
||||
|
fi |
||||
|
|
||||
|
if mkdir "$lock_dir" 2> /dev/null; then |
||||
|
readonly script_lock="$lock_dir" |
||||
|
verbose_print "Acquired script lock: $script_lock" |
||||
|
else |
||||
|
script_exit "Unable to acquire script lock: $lock_dir" 1 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
# DESC: Pretty print the provided string |
||||
|
# ARGS: $1 (required): Message to print (defaults to a green foreground) |
||||
|
# $2 (optional): Colour to print the message with. This can be an ANSI |
||||
|
# escape code or one of the prepopulated colour variables. |
||||
|
# $3 (optional): Set to any value to not append a new line to the message |
||||
|
# OUTS: None |
||||
|
function pretty_print() { |
||||
|
if [[ $# -lt 1 ]]; then |
||||
|
script_exit 'Missing required argument to pretty_print()!' 2 |
||||
|
fi |
||||
|
|
||||
|
if [[ -z ${no_colour-} ]]; then |
||||
|
if [[ -n ${2-} ]]; then |
||||
|
printf '%b' "$2" |
||||
|
else |
||||
|
printf '%b' "$fg_green" |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
# Print message & reset text attributes |
||||
|
if [[ -n ${3-} ]]; then |
||||
|
printf '%s%b' "$1" "$ta_none" |
||||
|
else |
||||
|
printf '%s%b\n' "$1" "$ta_none" |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
# DESC: Only pretty_print() the provided string if verbose mode is enabled |
||||
|
# ARGS: $@ (required): Passed through to pretty_print() function |
||||
|
# OUTS: None |
||||
|
function verbose_print() { |
||||
|
if [[ -n ${verbose-} ]]; then |
||||
|
pretty_print "$@" |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
# DESC: Combines two path variables and removes any duplicates |
||||
|
# ARGS: $1 (required): Path(s) to join with the second argument |
||||
|
# $2 (optional): Path(s) to join with the first argument |
||||
|
# OUTS: $build_path: The constructed path |
||||
|
# NOTE: Heavily inspired by: https://unix.stackexchange.com/a/40973 |
||||
|
function build_path() { |
||||
|
if [[ $# -lt 1 ]]; then |
||||
|
script_exit 'Missing required argument to build_path()!' 2 |
||||
|
fi |
||||
|
|
||||
|
local new_path path_entry temp_path |
||||
|
|
||||
|
temp_path="$1:" |
||||
|
if [[ -n ${2-} ]]; then |
||||
|
temp_path="$temp_path$2:" |
||||
|
fi |
||||
|
|
||||
|
new_path= |
||||
|
while [[ -n $temp_path ]]; do |
||||
|
path_entry="${temp_path%%:*}" |
||||
|
case "$new_path:" in |
||||
|
*:"$path_entry":*) ;; |
||||
|
*) |
||||
|
new_path="$new_path:$path_entry" |
||||
|
;; |
||||
|
esac |
||||
|
temp_path="${temp_path#*:}" |
||||
|
done |
||||
|
|
||||
|
# shellcheck disable=SC2034 |
||||
|
build_path="${new_path#:}" |
||||
|
} |
||||
|
|
||||
|
# DESC: Check a binary exists in the search path |
||||
|
# ARGS: $1 (required): Name of the binary to test for existence |
||||
|
# $2 (optional): Set to any value to treat failure as a fatal error |
||||
|
# OUTS: None |
||||
|
function check_binary() { |
||||
|
if [[ $# -lt 1 ]]; then |
||||
|
script_exit 'Missing required argument to check_binary()!' 2 |
||||
|
fi |
||||
|
|
||||
|
if ! command -v "$1" > /dev/null 2>&1; then |
||||
|
if [[ -n ${2-} ]]; then |
||||
|
script_exit "Missing dependency: Couldn't locate $1." 1 |
||||
|
else |
||||
|
verbose_print "Missing dependency: $1" "${fg_red-}" |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
verbose_print "Found dependency: $1" |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
# DESC: Validate we have superuser access as root (via sudo if requested) |
||||
|
# ARGS: $1 (optional): Set to any value to not attempt root access via sudo |
||||
|
# OUTS: None |
||||
|
function check_superuser() { |
||||
|
local superuser |
||||
|
if [[ $EUID -eq 0 ]]; then |
||||
|
superuser=true |
||||
|
elif [[ -z ${1-} ]]; then |
||||
|
# shellcheck disable=SC2310 |
||||
|
if check_binary sudo; then |
||||
|
verbose_print 'Sudo: Updating cached credentials ...' |
||||
|
if ! sudo -v; then |
||||
|
verbose_print "Sudo: Couldn't acquire credentials ..." \ |
||||
|
"${fg_red-}" |
||||
|
else |
||||
|
local test_euid |
||||
|
test_euid="$(sudo -H -- "$BASH" -c 'printf "%s" "$EUID"')" |
||||
|
if [[ $test_euid -eq 0 ]]; then |
||||
|
superuser=true |
||||
|
fi |
||||
|
fi |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
if [[ -z ${superuser-} ]]; then |
||||
|
verbose_print 'Unable to acquire superuser credentials.' "${fg_red-}" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
verbose_print 'Successfully acquired superuser credentials.' |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
# 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: None |
||||
|
function run_as_root() { |
||||
|
if [[ $# -eq 0 ]]; then |
||||
|
script_exit 'Missing required argument to run_as_root()!' 2 |
||||
|
fi |
||||
|
|
||||
|
if [[ ${1-} =~ ^0$ ]]; then |
||||
|
local skip_sudo=true |
||||
|
shift |
||||
|
fi |
||||
|
|
||||
|
if [[ $EUID -eq 0 ]]; then |
||||
|
"$@" |
||||
|
elif [[ -z ${skip_sudo-} ]]; then |
||||
|
sudo -H -- "$@" |
||||
|
else |
||||
|
script_exit "Unable to run requested command as root: $*" 1 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
# vim: syntax=sh cc=80 tw=79 ts=4 sw=4 sts=4 et sr |
Loading…
Reference in new issue