From 86300b68c4b5e6d1170945bf373aca5b27af6bfc Mon Sep 17 00:00:00 2001 From: Nathaniel Landau Date: Thu, 28 Oct 2021 17:59:34 -0400 Subject: [PATCH] Improve shellcheck compatibility --- .hooks/pre-commit.sh | 70 +++++++++------ README.md | 12 +-- template.sh | 157 ++++++++++++++++++++++++---------- template_standalone.sh | 25 +++--- utilities/alerts.bash | 15 ++-- utilities/arrays.bash | 4 +- utilities/checks.bash | 6 +- utilities/dates.bash | 71 +++++++-------- utilities/debug.bash | 2 +- utilities/files.bash | 22 ++--- utilities/macOS.bash | 5 +- utilities/misc.bash | 14 +-- utilities/services.bash | 14 +-- utilities/strings.bash | 42 ++++----- utilities/template_utils.bash | 7 +- 15 files changed, 286 insertions(+), 180 deletions(-) diff --git a/.hooks/pre-commit.sh b/.hooks/pre-commit.sh index 49ec7b9..74e09da 100755 --- a/.hooks/pre-commit.sh +++ b/.hooks/pre-commit.sh @@ -21,7 +21,8 @@ _mainScript_() { [[ $# == 0 ]] && fatal "Missing required argument to ${FUNCNAME[0]}" - local _gitDiffTmp="${TMP_DIR}/diff.txt" + local _gitDiffTmp + _gitDiffTmp="${TMP_DIR}/diff.txt" if [ -f "${STOP_WORD_FILE}" ]; then @@ -32,7 +33,7 @@ _mainScript_() { debug "$(basename "${1}"): Checking for stop words..." # remove blank lines from stopwords file - cat "${STOP_WORD_FILE}" | sed '/^$/d' >"${TMP_DIR}/pattern_file.txt" + sed '/^$/d' "${STOP_WORD_FILE}" >"${TMP_DIR}/pattern_file.txt" # Add diff to a temporary file git diff --cached -- "${1}" | grep '^+' >"${_gitDiffTmp}" @@ -60,9 +61,11 @@ _mainScript_() { # USAGE: # _ignoreSymlinks_ - local _gitIgnore="${GITROOT}/.gitignore" - local _haveSymlink=false + local _gitIgnore + local _haveSymlink local _f + _gitIgnore="${GITROOT}/.gitignore" + _haveSymlink=false debug "Checking for symlinks..." @@ -81,7 +84,7 @@ _mainScript_() { done # Work on files that were mistakenly staged - for f in $(git status --porcelain | grep '^A' | sed 's/^A //'); do + for _f in $(git status --porcelain | grep '^A' | sed 's/^A //'); do if [ -L "${_f}" ]; then if ! grep "${_f}" "${_gitIgnore}"; then if printf "\n%s" "${_f}" >>"${_gitIgnore}"; then @@ -115,7 +118,8 @@ _mainScript_() { [[ $# == 0 ]] && fatal "Missing required argument to ${FUNCNAME[0]}" - local _filename="$(_fileName_ "${1}")" + local _filename + _filename="$(_fileName_ "${1}")" if command -v yaml-lint >/dev/null; then debug "${_filename}: Linting YAML..." @@ -159,14 +163,22 @@ _mainScript_() { [[ $# == 0 ]] && fatal "Missing required argument to ${FUNCNAME[0]}" - local _filename="$(_fileName_ "${1}")" - + local _filename + _filename="$(_fileName_ "${1}")" if command -v shellcheck >/dev/null; then debug "${_filename}: Linting shellscript..." - if shellcheck --exclude=2016,2059,2001,2002,2148,1090,2162,2005,2034,2154,2086,2155,2181,2164,2120,2119,1083,1117,2207,1091 "${1}"; then - return 0 + if [[ ${_filename} == "*.j2" ]]; then + if shellcheck -x --exclude=1009,1054,1056,1072,1073,1083,2001,2148 "${1}"; then + return 0 + else + return 1 + fi else - return 1 + if shellcheck -x --exclude=2001,2148 "${1}"; then + return 0 + else + return 1 + fi fi else notice "Shellcheck not installed. Continuing..." @@ -187,9 +199,11 @@ _mainScript_() { [[ $# == 0 ]] && fatal "Missing required argument to ${FUNCNAME[0]}" - local _filename="$(_fileName_ "${1}")" + local _filename + _filename="$(_fileName_ "${1}")" + debug "${_filename}: Runing bats tests..." - if bats -t $1; then + if bats -t "$1"; then return 0 else return 1 @@ -198,7 +212,7 @@ _mainScript_() { _lintAnsible_() { # DESC: - # Lint Ansible YMAL files staged for commit. Requires ansible-lint to be installed. + # Lint Ansible YAML files staged for commit. Requires ansible-lint to be installed. # ARGS: # $1 (Required): Path to file # OUTS: @@ -209,7 +223,8 @@ _mainScript_() { [[ $# == 0 ]] && fatal "Missing required argument to ${FUNCNAME[0]}" - local _filename="$(_fileName_ "${1}")" + local _filename + _filename="$(_fileName_ "${1}")" if ! command -v ansible-lint &>/dev/null; then notice "ansible-lint not intstalled. Continuing..." @@ -409,7 +424,7 @@ _setColors_() { # OUTS: # None # USAGE: - # echo "${blue}Some text${reset}" + # printf "%s\n" "${blue}Some text${reset}" if tput setaf 1 >/dev/null 2>&1; then bold=$(tput bold) @@ -440,6 +455,7 @@ _setColors_() { bold="\033[4;37m" reset="\033[0m" underline="\033[4;37m" + # shellcheck disable=SC2034 reverse="" white="\033[0;37m" blue="\033[0;34m" @@ -497,9 +513,9 @@ _alert_() { _color="${purple}" elif [ "${_alertType}" == "header" ]; then _color="${bold}${white}${underline}" - elif [ ${_alertType} == "notice" ]; then + elif [ "${_alertType}" == "notice" ]; then _color="${bold}" - elif [ ${_alertType} == "input" ]; then + elif [ "${_alertType}" == "input" ]; then _color="${bold}${underline}" elif [ "${_alertType}" = "dryrun" ]; then _color="${blue}" @@ -534,9 +550,10 @@ _alert_() { [[ ! -f ${LOGFILE} ]] && touch "${LOGFILE}" # Don't use colors in logs - local cleanmessage="$(echo "${_message}" | sed -E 's/(\x1b)?\[(([0-9]{1,2})(;[0-9]{1,3}){0,2})?[mGK]//g')" + 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}" + printf "%s [%7s] %s %s\n" "$(date +"%b %d %R:%S")" "${_alertType}" "[$(/bin/hostname)]" "${_cleanmessage}" >>"${LOGFILE}" } # Write specified log level data to logfile @@ -611,7 +628,7 @@ _printFuncStack_() { _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 ;; esac - _funcStackResponse+=("${FUNCNAME[$_i]}:$(basename ${BASH_SOURCE[$_i]}):${BASH_LINENO[_i - 1]}") + _funcStackResponse+=("${FUNCNAME[$_i]}:$(basename "${BASH_SOURCE[$_i]}"):${BASH_LINENO[_i - 1]}") done printf "( " printf %s "${_funcStackResponse[0]}" @@ -645,7 +662,7 @@ _safeExit_() { fi trap - INT TERM EXIT - exit ${1:-0} + exit "${1:-0}" } _trapCleanup_() { @@ -659,7 +676,7 @@ _trapCleanup_() { # $5: Scriptname # $6: $BASH_SOURCE # USAGE: - # trap '_trapCleanup_ ${LINENO} ${BASH_LINENO} "${BASH_COMMAND}" "${FUNCNAME[*]}" "${0}" "${BASH_SOURCE[0]}"' EXIT INT TERM SIGINT SIGQUIT SIGTERM + # trap '_trapCleanup_ ${LINENO} ${BASH_LINENO} "${BASH_COMMAND}" "${FUNCNAME[*]}" "${0}" "${BASH_SOURCE[0]}"' EXIT INT TERM SIGINT SIGQUIT SIGTERM ERR # OUTS: # Exits script with error code 1 @@ -671,7 +688,9 @@ _trapCleanup_() { local _sourced="${6:-}" if [[ "$(declare -f "fatal")" && "$(declare -f "_printFuncStack_")" ]]; then - _funcstack="'$(echo "${_funcstack}" | sed -E 's/ / < /g')'" + + _funcstack="'$(printf "%s" "${_funcstack}" | sed -E 's/ / < /g')'" + if [[ ${_script##*/} == "${_sourced##*/}" ]]; then fatal "${7:-} command: '${_command}' (line: ${_line}) [func: $(_printFuncStack_)]" else @@ -760,7 +779,7 @@ _setPATH_() { for _newPath in "$@"; do if [ -d "${_newPath}" ]; then - if ! echo "${PATH}" | grep -Eq "(^|:)${_newPath}($|:)"; then + if ! printf "%s" "${PATH}" | grep -Eq "(^|:)${_newPath}($|:)"; then if PATH="${_newPath}:${PATH}"; then debug "Added '${_newPath}' to PATH" else @@ -850,6 +869,7 @@ _parseOptions_() { unset _options # Read the options and set stuff + # shellcheck disable=SC2034 while [[ ${1:-} == -?* ]]; do case $1 in # Custom options diff --git a/README.md b/README.md index 943894e..f2425d6 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ IFS=$' \n\t' # set -o xtrace # Source utility functions -_sourceUtilities_ "${HOME}/repos/shell-scripting-templates/utilities" +_sourceUtilities_ # Initialize color constants _setColors_ @@ -117,11 +117,11 @@ Within the `utilities` folder are many BASH functions meant to ease development #### 1. Copy and paste into standaloneTemplate.sh -You can copy any complete function from the Utilities and place it into your script. Copy it beneath the `### Custom utility functions` line. +You can copy any complete function from the Utilities and place it into your script. Copy it beneath the `### Custom utility functions` line. Scripts created this way are fully portable among systems #### 2. Source all the utility files by using template.sh -`template.sh` contains a function to source all the utility files into the script. Beware, this will require a full path to the location of this repository and will result in a script that will not be portable to other systems. +`template.sh` contains a function to source all the utility files into the script. Beware, that you'll need to update the paths within the `_sourceUtilities_` function to ensure your script can find this repository. ## alerts.bash @@ -304,14 +304,14 @@ Functions required to allow the script template and alert functions to be used # Coding conventions -Where possible, I follow [defensive BASH programming](https://kfirlavi.herokuapp.com/blog/2012/11/14/defensive-bash-programming/) principles. - - Function names use camel case surrounded by underscores: `_nameOfFunction_` - Local variable names use camel case with a starting underscore: `_localVariable` - Global variables are in ALL_CAPS with underscores seperating words - Exceptions to the variable an function naming rules are made for alerting functions and colors to ease my speed of programming. (Breaking years of habits is hard...) I.e. `notice "Some log item: ${blue}blue text${reset}` Where `notice` is a function and `$blue` and `$reset` are global variables but are lowercase. -- Variables are always surrounded by quotes and brackets `"${1}"` (It's verbose, but a safe practice) +- Variables are always surrounded by quotes and brackets `"${1}"` (Overly verbose true, but a safe practice) - Formatting is provided by [shfmt](https://github.com/mvdan/sh) using 4 spaces for indentation +- All scripts and functions are fully [Shellcheck](https://github.com/koalaman/shellcheck) compliant +- Where possible, I follow [defensive BASH programming](https://kfirlavi.herokuapp.com/blog/2012/11/14/defensive-bash-programming/) principles. ## A Note on Code Reuse and Prior Art diff --git a/template.sh b/template.sh index 13789c8..a912f99 100755 --- a/template.sh +++ b/template.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# shellcheck source-path=SCRIPTDIR/../shell-scripting-templates/utilities _mainScript_() { # Replace everything in _mainScript_() with your script's code @@ -52,7 +53,9 @@ _trapCleanup_() { local _sourced="${6:-}" if [[ "$(declare -f "fatal")" && "$(declare -f "_printFuncStack_")" ]]; then - _funcstack="'$(echo "${_funcstack}" | sed -E 's/ / < /g')'" + + _funcstack="'$(printf "%s" "${_funcstack}" | sed -E 's/ / < /g')'" + if [[ ${_script##*/} == "${_sourced##*/}" ]]; then fatal "${7:-} command: '${_command}' (line: ${_line}) [func: $(_printFuncStack_)]" else @@ -69,58 +72,123 @@ _trapCleanup_() { fi } +_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)" +} + _sourceUtilities_() { # DESC: - # Sources bash utility functions + # Sources utility functions. Absolute paths are required for shellcheck to correctly + # parse the sourced files # ARGS: - # $1 (required): Directories or files containing utility functions + # NONE # OUTS: - # 0 if success - # 1 if failure + # 0: Success + # 1: Failure # USAGE: - # _sourceHelperFiles_ "/path/to/dir" "path/to/file.sh" + # _sourceUtilities_ - local _filesSourced=true + local _utilsPath + _utilsPath="$(_findBaseDir_)/../shell-scripting-templates/utilities/" - [[ $# == 0 ]] && _filesSourced=false - - local _fileToSource - local _location - if [ ${_filesSourced} == true ]; then - for _location in "$@"; do - if [[ -d ${_location} ]]; then - for _fileToSource in "${_location}"/*.{sh,bash}; do - if [[ -f ${_fileToSource} ]]; then - if ! source "${_fileToSource}"; then - _filesSourced=false - break 2 - fi - else - _filesSourced=false - break 2 - fi - done - elif [[ -f ${_location} ]] && [[ ${_location} =~ .*\.(sh|bash)$ ]]; then - if ! source "${_fileToSource}"; then - _filesSourced=false - break - fi - else - _filesSourced=false - break - fi - done + if [ -f "${_utilsPath}/alerts.bash" ]; then + source "${_utilsPath}/alerts.bash" + else + printf "%s\n" "ERROR: alerts.bash not found" + exit 1 fi - if [ ${_filesSourced} == true ]; then - return 0 + if [ -f "${_utilsPath}/arrays.bash" ]; then + source "${_utilsPath}/arrays.bash" else - printf "%s\n" "ERROR: Invalid argument to ${FUNCNAME[0]}: ${_location}" - if [ "$(declare -f "_safeExit_")" ]; then - _safeExit_ 1 - else - exit 1 - fi + printf "%s\n" "ERROR: arrays.bash not found" + exit 1 + fi + + if [ -f "${_utilsPath}/checks.bash" ]; then + source "${_utilsPath}/checks.bash" + else + printf "%s\n" "ERROR: checks.bash not found" + exit 1 + fi + + if [ -f "${_utilsPath}/dates.bash" ]; then + source "${_utilsPath}/dates.bash" + else + printf "%s\n" "ERROR: dates.bash not found" + exit 1 + fi + + if [ -f "${_utilsPath}/debug.bash" ]; then + source "${_utilsPath}/debug.bash" + else + printf "%s\n" "ERROR: debug.bash not found" + exit 1 + fi + + if [ -f "${_utilsPath}/files.bash" ]; then + source "${_utilsPath}/files.bash" + else + printf "%s\n" "ERROR: files.bash not found" + exit 1 + fi + + if [ -f "${_utilsPath}/macOS.bash" ]; then + source "${_utilsPath}/macOS.bash" + else + printf "%s\n" "ERROR: macOS.bash not found" + exit 1 + fi + + if [ -f "${_utilsPath}/misc.bash" ]; then + source "${_utilsPath}/misc.bash" + else + printf "%s\n" "ERROR: misc.bash not found" + exit 1 + fi + + if [ -f "${_utilsPath}/services.bash" ]; then + source "${_utilsPath}/services.bash" + else + printf "%s\n" "ERROR: services.bash not found" + exit 1 + fi + + if [ -f "${_utilsPath}/strings.bash" ]; then + source "${_utilsPath}/strings.bash" + else + printf "%s\n" "ERROR: strings.bash not found" + exit 1 + fi + + if [ -f "${_utilsPath}/template_utils.bash" ]; then + source "${_utilsPath}/template_utils.bash" + else + printf "%s\n" "ERROR: template_utils.bash not found" + exit 1 fi } @@ -170,6 +238,7 @@ _parseOptions_() { unset _options # Read the options and set stuff + # shellcheck disable=SC2034 while [[ ${1:-} == -?* ]]; do case $1 in # Custom options @@ -268,7 +337,7 @@ IFS=$' \n\t' # set -o xtrace # Source utility functions -_sourceUtilities_ "${HOME}/repos/shell-scripting-templates/utilities" +_sourceUtilities_ # Initialize color constants _setColors_ diff --git a/template_standalone.sh b/template_standalone.sh index 6da958f..23ef6c2 100755 --- a/template_standalone.sh +++ b/template_standalone.sh @@ -40,7 +40,7 @@ _setColors_() { # OUTS: # None # USAGE: - # echo "${blue}Some text${reset}" + # printf "%s\n" "${blue}Some text${reset}" if tput setaf 1 >/dev/null 2>&1; then bold=$(tput bold) @@ -71,6 +71,7 @@ _setColors_() { bold="\033[4;37m" reset="\033[0m" underline="\033[4;37m" + # shellcheck disable=SC2034 reverse="" white="\033[0;37m" blue="\033[0;34m" @@ -128,9 +129,9 @@ _alert_() { _color="${purple}" elif [ "${_alertType}" == "header" ]; then _color="${bold}${white}${underline}" - elif [ ${_alertType} == "notice" ]; then + elif [ "${_alertType}" == "notice" ]; then _color="${bold}" - elif [ ${_alertType} == "input" ]; then + elif [ "${_alertType}" == "input" ]; then _color="${bold}${underline}" elif [ "${_alertType}" = "dryrun" ]; then _color="${blue}" @@ -165,9 +166,10 @@ _alert_() { [[ ! -f ${LOGFILE} ]] && touch "${LOGFILE}" # Don't use colors in logs - local cleanmessage="$(echo "${_message}" | sed -E 's/(\x1b)?\[(([0-9]{1,2})(;[0-9]{1,3}){0,2})?[mGK]//g')" + 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}" + printf "%s [%7s] %s %s\n" "$(date +"%b %d %R:%S")" "${_alertType}" "[$(/bin/hostname)]" "${_cleanmessage}" >>"${LOGFILE}" } # Write specified log level data to logfile @@ -242,7 +244,7 @@ _printFuncStack_() { _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 ;; esac - _funcStackResponse+=("${FUNCNAME[$_i]}:$(basename ${BASH_SOURCE[$_i]}):${BASH_LINENO[_i - 1]}") + _funcStackResponse+=("${FUNCNAME[$_i]}:$(basename "${BASH_SOURCE[$_i]}"):${BASH_LINENO[_i - 1]}") done printf "( " printf %s "${_funcStackResponse[0]}" @@ -276,7 +278,7 @@ _safeExit_() { fi trap - INT TERM EXIT - exit ${1:-0} + exit "${1:-0}" } _trapCleanup_() { @@ -290,7 +292,7 @@ _trapCleanup_() { # $5: Scriptname # $6: $BASH_SOURCE # USAGE: - # trap '_trapCleanup_ ${LINENO} ${BASH_LINENO} "${BASH_COMMAND}" "${FUNCNAME[*]}" "${0}" "${BASH_SOURCE[0]}"' EXIT INT TERM SIGINT SIGQUIT SIGTERM + # trap '_trapCleanup_ ${LINENO} ${BASH_LINENO} "${BASH_COMMAND}" "${FUNCNAME[*]}" "${0}" "${BASH_SOURCE[0]}"' EXIT INT TERM SIGINT SIGQUIT SIGTERM ERR # OUTS: # Exits script with error code 1 @@ -302,7 +304,9 @@ _trapCleanup_() { local _sourced="${6:-}" if [[ "$(declare -f "fatal")" && "$(declare -f "_printFuncStack_")" ]]; then - _funcstack="'$(echo "${_funcstack}" | sed -E 's/ / < /g')'" + + _funcstack="'$(printf "%s" "${_funcstack}" | sed -E 's/ / < /g')'" + if [[ ${_script##*/} == "${_sourced##*/}" ]]; then fatal "${7:-} command: '${_command}' (line: ${_line}) [func: $(_printFuncStack_)]" else @@ -391,7 +395,7 @@ _setPATH_() { for _newPath in "$@"; do if [ -d "${_newPath}" ]; then - if ! echo "${PATH}" | grep -Eq "(^|:)${_newPath}($|:)"; then + if ! printf "%s" "${PATH}" | grep -Eq "(^|:)${_newPath}($|:)"; then if PATH="${_newPath}:${PATH}"; then debug "Added '${_newPath}' to PATH" else @@ -481,6 +485,7 @@ _parseOptions_() { unset _options # Read the options and set stuff + # shellcheck disable=SC2034 while [[ ${1:-} == -?* ]]; do case $1 in # Custom options diff --git a/utilities/alerts.bash b/utilities/alerts.bash index b8cb53f..a155481 100644 --- a/utilities/alerts.bash +++ b/utilities/alerts.bash @@ -1,4 +1,5 @@ # Functions for providing alerts to the user and logging them +# shellcheck disable=SC2034,SC2154 _setColors_() { # DESC: @@ -8,7 +9,7 @@ _setColors_() { # OUTS: # None # USAGE: - # echo "${blue}Some text${reset}" + # printf "%s\n" "${blue}Some text${reset}" if tput setaf 1 >/dev/null 2>&1; then bold=$(tput bold) @@ -39,6 +40,7 @@ _setColors_() { bold="\033[4;37m" reset="\033[0m" underline="\033[4;37m" + # shellcheck disable=SC2034 reverse="" white="\033[0;37m" blue="\033[0;34m" @@ -96,9 +98,9 @@ _alert_() { _color="${purple}" elif [ "${_alertType}" == "header" ]; then _color="${bold}${white}${underline}" - elif [ ${_alertType} == "notice" ]; then + elif [ "${_alertType}" == "notice" ]; then _color="${bold}" - elif [ ${_alertType} == "input" ]; then + elif [ "${_alertType}" == "input" ]; then _color="${bold}${underline}" elif [ "${_alertType}" = "dryrun" ]; then _color="${blue}" @@ -133,9 +135,10 @@ _alert_() { [[ ! -f ${LOGFILE} ]] && touch "${LOGFILE}" # Don't use colors in logs - local cleanmessage="$(echo "${_message}" | sed -E 's/(\x1b)?\[(([0-9]{1,2})(;[0-9]{1,3}){0,2})?[mGK]//g')" + 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}" + printf "%s [%7s] %s %s\n" "$(date +"%b %d %R:%S")" "${_alertType}" "[$(/bin/hostname)]" "${_cleanmessage}" >>"${LOGFILE}" } # Write specified log level data to logfile @@ -210,7 +213,7 @@ _printFuncStack_() { _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 ;; esac - _funcStackResponse+=("${FUNCNAME[$_i]}:$(basename ${BASH_SOURCE[$_i]}):${BASH_LINENO[_i - 1]}") + _funcStackResponse+=("${FUNCNAME[$_i]}:$(basename "${BASH_SOURCE[$_i]}"):${BASH_LINENO[_i - 1]}") done printf "( " printf %s "${_funcStackResponse[0]}" diff --git a/utilities/arrays.bash b/utilities/arrays.bash index d7e7f74..209cec2 100644 --- a/utilities/arrays.bash +++ b/utilities/arrays.bash @@ -190,7 +190,7 @@ _forEachReject_() { fi declare -i _ret=$? if [[ ${_ret} -ne 0 ]]; then - echo "${_it}" + printf "%s\n" "${_it}" fi done } @@ -211,7 +211,7 @@ _forEachSome_() { [[ $# == 0 ]] && fatal "Missing required argument to ${FUNCNAME[0]}" local _func="${1}" - local _IFS=$'\n' + local IFS=$'\n' while read -r _it; do if [[ ${_func} == *"$"* ]]; then diff --git a/utilities/checks.bash b/utilities/checks.bash index f34d871..bbe7b02 100644 --- a/utilities/checks.bash +++ b/utilities/checks.bash @@ -135,7 +135,7 @@ _isFQDN_() { local _input="${1}" - if printf "${_input}" | grep -Pq '(?=^.{4,253}$)(^(?:[a-zA-Z0-9](?:(?:[a-zA-Z0-9\-]){0,61}[a-zA-Z0-9])?\.)+([a-zA-Z]{2,}|xn--[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])$)'; then + if printf "%s" "${_input}" | grep -Pq '(?=^.{4,253}$)(^(?:[a-zA-Z0-9](?:(?:[a-zA-Z0-9\-]){0,61}[a-zA-Z0-9])?\.)+([a-zA-Z]{2,}|xn--[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])$)'; then return 0 else return 1 @@ -218,7 +218,7 @@ _isDir_() { # 1 - Input is not a directory # USAGE: # _varIsDir_ "${var}" - # (_isDir_ "${var}") && echo "Is a directory" || echo "Not a directory" + # (_isDir_ "${var}") && printf "Is a directory" || printf "Not a directory" # NOTES: # @@ -274,7 +274,7 @@ _rootAvailable_() { # https://github.com/ralish/bash-script-template local _superuser - local _testEUID + if [[ ${EUID} -eq 0 ]]; then _superuser=true elif [[ -z ${1:-} ]]; then diff --git a/utilities/dates.bash b/utilities/dates.bash index 26735c6..ea119af 100644 --- a/utilities/dates.bash +++ b/utilities/dates.bash @@ -46,7 +46,7 @@ _countdown_() { else echo "${_message} ${ii}" fi - sleep ${_sleepTime} + sleep "${_sleepTime}" done } @@ -120,7 +120,7 @@ _fromSeconds_() { ((_h = ${1} / 3600)) ((_m = (${1} % 3600) / 60)) ((_s = ${1} % 60)) - printf "%02d:%02d:%02d\n" ${_h} ${_m} ${_s} + printf "%02d:%02d:%02d\n" "${_h}" "${_m}" "${_s}" } _monthToNumber_() { @@ -135,7 +135,9 @@ _monthToNumber_() { [[ $# == 0 ]] && fatal "Missing required argument to ${FUNCNAME[0]}" - local _mon="$(echo "$1" | tr '[:upper:]' '[:lower:]')" + local _mon + _mon="$(echo "$1" | tr '[:upper:]' '[:lower:]')" + case "${_mon}" in january | jan | ja) echo 1 ;; february | feb | fe) echo 2 ;; @@ -232,7 +234,7 @@ _parseDate_() { trap "$(shopt -p nocasematch)" RETURN # reset nocasematch when function exits shopt -s nocasematch # Use case-insensitive regex - debug "_parseDate_() input ${tan}${_stringToTest}${purple}" + debug "_parseDate_() input: ${_stringToTest}" # YYYY MM DD or YYYY-MM-DD _pat="(.*[^0-9]|^)((20[0-2][0-9])[-\.\/_ ]+([0-9]{1,2})[-\.\/_ ]+([0-9]{1,2}))([^0-9].*|$)" @@ -242,25 +244,25 @@ _parseDate_() { PARSE_DATE_MONTH=$((10#${BASH_REMATCH[4]})) PARSE_DATE_MONTH_NAME="$(_numberToMonth_ "${PARSE_DATE_MONTH}")" PARSE_DATE_DAY=$((10#${BASH_REMATCH[5]})) - debug "regex match: ${tan}YYYY-MM-DD${purple}" + debug "regex match: YYYY-MM-DD " # Month DD, YYYY elif [[ ${_stringToTest} =~ ((january|jan|ja|february|feb|fe|march|mar|ma|april|apr|ap|may|june|jun|july|jul|ju|august|aug|september|sep|october|oct|november|nov|december|dec)[-\./_ ]+([0-9]{1,2})(nd|rd|th|st)?,?[-\./_ ]+(20[0-2][0-9]))([^0-9].*|$) ]]; then PARSE_DATE_FOUND="${BASH_REMATCH[1]:-}" - PARSE_DATE_MONTH=$(_monthToNumber_ ${BASH_REMATCH[2]:-}) + PARSE_DATE_MONTH=$(_monthToNumber_ "${BASH_REMATCH[2]:-}") PARSE_DATE_MONTH_NAME="$(_numberToMonth_ "${PARSE_DATE_MONTH:-}")" PARSE_DATE_DAY=$((10#${BASH_REMATCH[3]:-})) PARSE_DATE_YEAR=$((10#${BASH_REMATCH[5]:-})) - debug "regex match: ${tan}Month DD, YYYY${purple}" + debug "regex match: Month DD, YYYY" # Month DD, YY elif [[ ${_stringToTest} =~ ((january|jan|ja|february|feb|fe|march|mar|ma|april|apr|ap|may|june|jun|july|jul|ju|august|aug|september|sep|october|oct|november|nov|december|dec)[-\./_ ]+([0-9]{1,2})(nd|rd|th|st)?,?[-\./_ ]+([0-9]{2}))([^0-9].*|$) ]]; then PARSE_DATE_FOUND="${BASH_REMATCH[1]}" - PARSE_DATE_MONTH=$(_monthToNumber_ ${BASH_REMATCH[2]}) + PARSE_DATE_MONTH=$(_monthToNumber_ "${BASH_REMATCH[2]}") PARSE_DATE_MONTH_NAME="$(_numberToMonth_ "${PARSE_DATE_MONTH}")" PARSE_DATE_DAY=$((10#${BASH_REMATCH[3]})) PARSE_DATE_YEAR="20$((10#${BASH_REMATCH[5]}))" - debug "regex match: ${tan}Month DD, YY${purple}" + debug "regex match: Month DD, YY" # DD Month YYYY elif [[ ${_stringToTest} =~ (.*[^0-9]|^)(([0-9]{2})[-\./_ ]+(january|jan|ja|february|feb|fe|march|mar|ma|april|apr|ap|may|june|jun|july|jul|ju|august|aug|september|sep|october|oct|november|nov|december|dec),?[-\./_ ]+(20[0-2][0-9]))([^0-9].*|$) ]]; then @@ -269,7 +271,7 @@ _parseDate_() { PARSE_DATE_MONTH="$(_monthToNumber_ "${BASH_REMATCH[4]}")" PARSE_DATE_MONTH_NAME="$(_numberToMonth_ "${PARSE_DATE_MONTH}")" PARSE_DATE_YEAR=$((10#"${BASH_REMATCH[5]}")) - debug "regex match: ${tan}DD Month, YYYY${purple}" + debug "regex match: DD Month, YYYY" # MM-DD-YYYY or DD-MM-YYYY elif [[ ${_stringToTest} =~ (.*[^0-9]|^)(([0-9]{1,2})[-\.\/_ ]+([0-9]{1,2})[-\.\/_ ]+(20[0-2][0-9]))([^0-9].*|$) ]]; then @@ -283,7 +285,7 @@ _parseDate_() { PARSE_DATE_MONTH=$((10#${BASH_REMATCH[3]})) PARSE_DATE_MONTH_NAME="$(_numberToMonth_ "${PARSE_DATE_MONTH}")" PARSE_DATE_DAY=$((10#${BASH_REMATCH[4]})) - debug "regex match: ${tan}MM-DD-YYYY${purple}" + debug "regex match: MM-DD-YYYY" elif [[ $((10#${BASH_REMATCH[3]})) -gt 12 && $((10#${BASH_REMATCH[3]})) -lt 32 && $((10#${BASH_REMATCH[4]})) -lt 13 ]] \ @@ -293,7 +295,7 @@ _parseDate_() { PARSE_DATE_MONTH=$((10#${BASH_REMATCH[4]})) PARSE_DATE_MONTH_NAME="$(_numberToMonth_ "${PARSE_DATE_MONTH}")" PARSE_DATE_DAY=$((10#${BASH_REMATCH[3]})) - debug "regex match: ${tan}DD-MM-YYYY${purple}" + debug "regex match: DD-MM-YYYY" elif [[ $((10#${BASH_REMATCH[3]})) -lt 32 && $((10#${BASH_REMATCH[4]})) -lt 13 ]] \ ; then @@ -302,7 +304,7 @@ _parseDate_() { PARSE_DATE_MONTH=$((10#${BASH_REMATCH[3]})) PARSE_DATE_MONTH_NAME="$(_numberToMonth_ "${PARSE_DATE_MONTH}")" PARSE_DATE_DAY=$((10#${BASH_REMATCH[4]})) - debug "regex match: ${tan}MM-DD-YYYY${purple}" + debug "regex match: MM-DD-YYYY" else shopt -u nocasematch return 1 @@ -319,7 +321,7 @@ _parseDate_() { PARSE_DATE_MONTH=$((10#${BASH_REMATCH[3]})) PARSE_DATE_MONTH_NAME="$(_numberToMonth_ "${PARSE_DATE_MONTH}")" PARSE_DATE_DAY=$((10#${BASH_REMATCH[4]})) - debug "regex match: ${tan}MM-DD-YYYY${purple}" + debug "regex match: MM-DD-YYYY" elif [[ $((10#${BASH_REMATCH[3]})) -gt 12 && $((10#${BASH_REMATCH[3]})) -lt 32 && $((10#${BASH_REMATCH[4]})) -lt 13 ]] \ @@ -329,7 +331,7 @@ _parseDate_() { PARSE_DATE_MONTH=$((10#${BASH_REMATCH[4]})) PARSE_DATE_MONTH_NAME="$(_numberToMonth_ "${PARSE_DATE_MONTH}")" PARSE_DATE_DAY=$((10#${BASH_REMATCH[3]})) - debug "regex match: ${tan}DD-MM-YYYY${purple}" + debug "regex match: DD-MM-YYYY" elif [[ $((10#${BASH_REMATCH[3]})) -lt 32 && $((10#${BASH_REMATCH[4]})) -lt 13 ]] \ ; then @@ -338,7 +340,7 @@ _parseDate_() { PARSE_DATE_MONTH=$((10#${BASH_REMATCH[3]})) PARSE_DATE_MONTH_NAME="$(_numberToMonth_ "${PARSE_DATE_MONTH}")" PARSE_DATE_DAY=$((10#${BASH_REMATCH[4]})) - debug "regex match: ${tan}MM-DD-YYYY${purple}" + debug "regex match: MM-DD-YYYY" else shopt -u nocasematch return 1 @@ -349,9 +351,9 @@ _parseDate_() { PARSE_DATE_FOUND="${BASH_REMATCH[1]}" PARSE_DATE_DAY="1" PARSE_DATE_MONTH="$(_monthToNumber_ "${BASH_REMATCH[2]}")" - PARSE_DATE_MONTH_NAME="$(_numberToMonth_ $PARSE_DATE_MONTH)" + PARSE_DATE_MONTH_NAME="$(_numberToMonth_ "${PARSE_DATE_MONTH}")" PARSE_DATE_YEAR="$((10#${BASH_REMATCH[3]}))" - debug "regex match: ${tan}Month, YYYY${purple}" + debug "regex match: Month, YYYY" # YYYYMMDDHHMM elif [[ ${_stringToTest} =~ (.*[^0-9]|^)((20[0-2][0-9])([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2}))([^0-9].*|$) ]]; then @@ -362,7 +364,7 @@ _parseDate_() { PARSE_DATE_YEAR="$((10#${BASH_REMATCH[3]}))" PARSE_DATE_HOUR="$((10#${BASH_REMATCH[6]}))" PARSE_DATE_MINUTE="$((10#${BASH_REMATCH[7]}))" - debug "regex match: ${tan}YYYYMMDDHHMM${purple}" + debug "regex match: YYYYMMDDHHMM" # YYYYMMDDHH 1 2 3 4 5 6 elif [[ ${_stringToTest} =~ (.*[^0-9]|^)((20[0-2][0-9])([0-9]{2})([0-9]{2})([0-9]{2}))([^0-9].*|$) ]]; then @@ -373,7 +375,7 @@ _parseDate_() { PARSE_DATE_YEAR="$((10#${BASH_REMATCH[3]}))" PARSE_DATE_HOUR="${BASH_REMATCH[6]}" PARSE_DATE_MINUTE="00" - debug "regex match: ${tan}YYYYMMDDHHMM${purple}" + debug "regex match: YYYYMMDDHHMM" # MMDDYYYY or YYYYMMDD or DDMMYYYY # 1 2 3 4 5 6 @@ -389,7 +391,7 @@ _parseDate_() { PARSE_DATE_MONTH="$((10#${BASH_REMATCH[3]}))" PARSE_DATE_MONTH_NAME="$(_numberToMonth_ "${PARSE_DATE_MONTH}")" PARSE_DATE_YEAR="${BASH_REMATCH[5]}${BASH_REMATCH[6]}" - debug "regex match: ${tan}MMDDYYYY${purple}" + debug "regex match: MMDDYYYY" # DDMMYYYY elif [[ $((10#${BASH_REMATCH[5]})) -eq 20 && $((10#${BASH_REMATCH[3]})) -gt 12 && @@ -401,7 +403,7 @@ _parseDate_() { PARSE_DATE_MONTH="$((10#${BASH_REMATCH[4]}))" PARSE_DATE_MONTH_NAME="$(_numberToMonth_ "${PARSE_DATE_MONTH}")" PARSE_DATE_YEAR="${BASH_REMATCH[5]}${BASH_REMATCH[6]}" - debug "regex match: ${tan}DDMMYYYY${purple}" + debug "regex match: DDMMYYYY" # YYYYMMDD elif [[ $((10#${BASH_REMATCH[3]})) -eq 20 && $((10#${BASH_REMATCH[6]})) -gt 12 && @@ -413,7 +415,7 @@ _parseDate_() { PARSE_DATE_MONTH="$((10#${BASH_REMATCH[5]}))" PARSE_DATE_MONTH_NAME="$(_numberToMonth_ "${PARSE_DATE_MONTH}")" PARSE_DATE_YEAR="${BASH_REMATCH[3]}${BASH_REMATCH[4]}" - debug "regex match: ${tan}YYYYMMDD${purple}" + debug "regex match: YYYYMMDD" # YYYYDDMM elif [[ $((10#${BASH_REMATCH[3]})) -eq 20 && $((10#${BASH_REMATCH[5]})) -gt 12 && @@ -425,7 +427,7 @@ _parseDate_() { PARSE_DATE_MONTH="$((10#${BASH_REMATCH[6]}))" PARSE_DATE_MONTH_NAME="$(_numberToMonth_ "${PARSE_DATE_MONTH}")" PARSE_DATE_YEAR="${BASH_REMATCH[3]}${BASH_REMATCH[4]}" - debug "regex match: ${tan}YYYYMMDD${purple}" + debug "regex match: YYYYMMDD" # Assume YYYMMDD elif [[ $((10#${BASH_REMATCH[3]})) -eq 20 && $((10#${BASH_REMATCH[6]})) -lt 32 && @@ -436,7 +438,7 @@ _parseDate_() { PARSE_DATE_MONTH="$((10#${BASH_REMATCH[5]}))" PARSE_DATE_MONTH_NAME="$(_numberToMonth_ "${PARSE_DATE_MONTH}")" PARSE_DATE_YEAR="${BASH_REMATCH[3]}${BASH_REMATCH[4]}" - debug "regex match: ${tan}YYYYMMDD${purple}" + debug "regex match: YYYYMMDD" else shopt -u nocasematch return 1 @@ -485,13 +487,13 @@ _parseDate_() { return 1 } - debug "${tan}\$PARSE_DATE_FOUND: ${PARSE_DATE_FOUND}${purple}" - debug "${tan}\$PARSE_DATE_YEAR: ${PARSE_DATE_YEAR}${purple}" - debug "${tan}\$PARSE_DATE_MONTH: ${PARSE_DATE_MONTH}${purple}" - debug "${tan}\$PARSE_DATE_MONTH_NAME: ${PARSE_DATE_MONTH_NAME}${purple}" - debug "${tan}\$PARSE_DATE_DAY: ${PARSE_DATE_DAY}${purple}" - [[ -z ${PARSE_DATE_HOUR:-} ]] || debug "${tan}\$PARSE_DATE_HOUR: ${PARSE_DATE_HOUR}${purple}" - [[ -z ${PARSE_DATE_MINUTE:-} ]] || debug "${tan}\$PARSE_DATE_MINUTE: ${PARSE_DATE_MINUTE}${purple}" + debug "\$PARSE_DATE_FOUND: ${PARSE_DATE_FOUND}" + debug "\$PARSE_DATE_YEAR: ${PARSE_DATE_YEAR}" + debug "\$PARSE_DATE_MONTH: ${PARSE_DATE_MONTH}" + debug "\$PARSE_DATE_MONTH_NAME: ${PARSE_DATE_MONTH_NAME}" + debug "\$PARSE_DATE_DAY: ${PARSE_DATE_DAY}" + [[ -z ${PARSE_DATE_HOUR:-} ]] || debug "\$PARSE_DATE_HOUR: ${PARSE_DATE_HOUR}" + [[ -z ${PARSE_DATE_MINUTE:-} ]] || debug "\$PARSE_DATE_MINUTE: ${PARSE_DATE_MINUTE}" shopt -u nocasematch @@ -527,7 +529,8 @@ _readableUnixTimestamp_() { [[ $# == 0 ]] && fatal "Missing required argument to ${FUNCNAME[0]}" local _timestamp="${1}" local _format="${2:-"%F %T"}" - local _out="$(date -d "@${_timestamp}" +"${_format}")" || return 1 + local _out + _out="$(date -d "@${_timestamp}" +"${_format}")" || return 1 printf "%s\n" "${_out}" } @@ -559,7 +562,7 @@ _toSeconds_() { if [[ $1 =~ [0-9]{1,2}(:|,|-|_|,| |[hHmMsS])[0-9]{1,2}(:|,|-|_|,| |[hHmMsS])[0-9]{1,2} ]]; then _saveIFS="${IFS}" - IFS=":,;-_, HhMmSs" read -r h m s <<<"$1" + IFS=":,;-_, HhMmSs" read -r _h _m _s <<<"$1" IFS="${_saveIFS}" else _h="$1" diff --git a/utilities/debug.bash b/utilities/debug.bash index b28cc93..a237c6f 100644 --- a/utilities/debug.bash +++ b/utilities/debug.bash @@ -31,7 +31,7 @@ _printAnsi_() { [[ $# == 0 ]] && fatal "Missing required argument to ${FUNCNAME[0]}" - #echo $(tr -dc '[:print:]'<<<$1) + #printf "%s\n" "$(tr -dc '[:print:]'<<<$1)" printf "%s\n" "${1//$'\e'/\\e}" } diff --git a/utilities/files.bash b/utilities/files.bash index 1d0eed3..240debf 100644 --- a/utilities/files.bash +++ b/utilities/files.bash @@ -99,7 +99,7 @@ _createUniqueFilename_() { # USAGE: # _createUniqueFilename_ "/some/dir/file.txt" --> /some/dir/file.txt.1 # _createUniqueFilename_ -i"/some/dir/file.txt" "-" --> /some/dir/file-1.txt - # echo "line" > "$(_createUniqueFilename_ "/some/dir/file.txt")" + # printf "%s" "line" > "$(_createUniqueFilename_ "/some/dir/file.txt")" [[ $# -lt 1 ]] && fatal "Missing required argument to ${FUNCNAME[0]}" @@ -155,7 +155,7 @@ _createUniqueFilename_() { _fn="${_originalFile}" for ((i = 0; i < _levels; i++)); do _ext=${_fn##*.} - if [ $i == 0 ]; then + if [[ $i == 0 ]]; then _extension=${_ext}${_extension:-} else _extension=${_ext}.${_extension:-} @@ -187,7 +187,7 @@ _createUniqueFilename_() { fi fi - echo "${_newFilename}" + printf "%s\n" "${_newFilename}" return 0 } @@ -391,7 +391,7 @@ _fileExtension_() { _fn="$_file" for ((i = 0; i < _levels; i++)); do _ext=${_fn##*.} - if [ $i == 0 ]; then + if [[ $i == 0 ]]; then _exts=${_ext}${_exts:-} else _exts=${_ext}.${_exts:-} @@ -490,7 +490,7 @@ _listFiles_() { local _searchType="${1}" local _pattern="${2}" local _directory="${3:-.}" - local _fileMatch e + local _fileMatch declare -a _matchedFiles=() case "${_searchType}" in @@ -661,7 +661,9 @@ _parseYAML_() { local _s='[[:space:]]*' local _w='[a-zA-Z0-9_]*' - local _fs="$(echo @ | tr @ '\034')" + local _fs + _fs="$(printf @ | tr @ '\034')" + sed -ne "s|^\(${_s}\)\(${_w}\)${_s}:${_s}\"\(.*\)\"${_s}\$|\1${_fs}\2${_fs}\3|p" \ -e "s|^\(${_s}\)\(${_w}\)${_s}[:-]${_s}\(.*\)${_s}\$|\1${_fs}\2${_fs}\3|p" "${_yamlFile}" \ | awk -F"${_fs}" '{ @@ -693,12 +695,12 @@ _readFile_() { [ ! -f "$_fileToRead" ] \ && { - echo "'$c' not found" + error "'${_fileToRead}' not found" return 1 } - while read -r result; do - printf "%s\n" "${result}" + while read -r _result; do + printf "%s\n" "${_result}" done <"${_fileToRead}" } @@ -716,7 +718,7 @@ _sourceFile_() { local _fileToSource="$1" [ ! -f "${_fileToSource}" ] && fatal "Attempted to source '${_fileToSource}'. Not found" - + # shellcheck disable=SC1090 if source "${_fileToSource}"; then return 0 else diff --git a/utilities/macOS.bash b/utilities/macOS.bash index 8e6fe4b..3a5d02e 100644 --- a/utilities/macOS.bash +++ b/utilities/macOS.bash @@ -31,7 +31,8 @@ _guiInput_() { # https://github.com/herrbischoff/awesome-osx-command-line/blob/master/functions.md if _haveScriptableFinder_; then local _guiPrompt="${1:-Password:}" - local _guiInput=$( + local _guiInput + _guiInput=$( osascript &>/dev/null <