diff --git a/bashutils.sh b/bashutils.sh index 10f3754..f131fb6 100755 --- a/bashutils.sh +++ b/bashutils.sh @@ -25,88 +25,62 @@ # unless it licensed under the same license without modifications. -# Environment control -## => Restores the environment -## => Arguments: none -## => Returns: 0 -function _bashutils_restore_environment() { - if [ -n "${_BASHUTILS_ENVIRONMENT_PREVIOUS_OPTIONS}" ]; then - [[ "${_BASHUTILS_ENVIRONMENT_PREVIOUS_OPTIONS}" == *"e"* ]] && set -e - [[ "${_BASHUTILS_ENVIRONMENT_PREVIOUS_OPTIONS}" != *"e"* ]] && set +e - [[ "${_BASHUTILS_ENVIRONMENT_PREVIOUS_OPTIONS}" == *"u"* ]] && set -u - [[ "${_BASHUTILS_ENVIRONMENT_PREVIOUS_OPTIONS}" != *"u"* ]] && set +u - [[ "${_BASHUTILS_ENVIRONMENT_PREVIOUS_OPTIONS}" == *"x"* ]] && set -x - [[ "${_BASHUTILS_ENVIRONMENT_PREVIOUS_OPTIONS}" != *"x"* ]] && set +x - [ -n "${_BASHUTILS_ENVIRONMENT_PREVIOUS_PIPEFAIL}" ] && set -o pipefail - [ -z "${_BASHUTILS_ENVIRONMENT_PREVIOUS_PIPEFAIL}" ] && set +o pipefail - unset _BASHUTILS_ENVIRONMENT_PREVIOUS_OPTIONS - fi - - return 0 -} -## => Resets the environment to optimal conditions for bashutils -## => Arguments: -## -> 1. none -## -> 2. [?...:command to execute] -## => Returns: 0 or the command's exit code if one is supplied -function _bashutils_reset_environment() { - export "_BASHUTILS_ENVIRONMENT_PREVIOUS_OPTIONS=${-}" - [ -o "pipefail" ] && export "_BASHUTILS_ENVIRONMENT_PREVIOUS_PIPEFAIL=true" - [ ! -o "pipefail" ] && export "_BASHUTILS_ENVIRONMENT_PREVIOUS_PIPEFAIL=" - set -eo pipefail - set +ux - - if [ -n "${*}" ]; then - # shellcheck disable=SC2068 - ${@} - EXITCODE="${?}" - _bashutils_restore_environment - return "${EXITCODE}" - fi - - return 0 -} -## => Resets the environment and returns the first argument. -## This is useful in situations where the environment shall -## be reset and the exit code preserved. -## => Arguments: -## => Returns: the provided exit code -function _bashutils_return_environment() { - _bashutils_restore_environment - return "${1:-0}" -} - - # Logging +## => Handles log calls and prints the specified message correctly. +## => Arguments: +## => Returns: 0 +function _bashutils_log() { + # Cancel if disallowed + [ ! "${BASHUTILS_LOGLEVEL}" -lt "${1}" ] && return + + # Set variables + # -> Level + #LEVEL_ID=${1} + LEVEL_NAME=${2} + # -> Message + shift 2 + MESSAGE=${*} + # -> Indentation + unset INDENTATION + for ((i=1; i <= BASHUTILS_SUBSHELL; i++)) + do + INDENTATION="${INDENTATION}${BASHUTILS_LOGINDENTATION}" + done + + # Add shadow + MESSAGE=${MESSAGE//\\n/\\n } + + # Print + echo -e "${LEVEL_NAME} ${INDENTATION} ${MESSAGE}"; +} ## => Prints a diagnostic message. ## => Arguments: ## => Returns: 0 -function diag() { [ "${BASHUTILS_LOGLEVEL}" -lt 1 ] && echo -e "DIAG ${*//\\n/\\n }"; } +function diag() { _bashutils_log "1" "DIAG" "${*}"; } ## => Prints a verbose message. ## => Arguments: ## => Returns: 0 -function verb() { [ "${BASHUTILS_LOGLEVEL}" -lt 2 ] && echo -e "VERB ${*//\\n/\\n }"; } +function verb() { _bashutils_log "2" "VERB" "${*}"; } ## => Prints a silent warning message. ## => Arguments: ## => Returns: 0 -function sarn() { [ "${BASHUTILS_LOGLEVEL}" -lt 3 ] && echo -e "SARN ${*//\\n/\\n }"; } +function sarn() { _bashutils_log "3" "SARN" "${*}"; } ## => Prints an informational message. ## => Arguments: ## => Returns: 0 -function info() { [ "${BASHUTILS_LOGLEVEL}" -lt 4 ] && echo -e "INFO ${*//\\n/\\n }"; } +function info() { _bashutils_log "4" "INFO" "${*}"; } ## => Prints a warning message. ## => Arguments: ## => Returns: 0 -function warn() { [ "${BASHUTILS_LOGLEVEL}" -lt 5 ] && echo -e "WARN ${*//\\n/\\n }"; } +function warn() { _bashutils_log "5" "WARN" "${*}"; } ## => Prints an error message. ## => Arguments: ## => Returns: 0 -function error() { [ "${BASHUTILS_LOGLEVEL}" -lt 6 ] && echo -e "ERR! ${*//\\n/\\n }" &> /dev/stderr; } +function error() { _bashutils_log "6" "ERR!" "${*}" &> /dev/stderr; } ## => Handles crashes. ## => Arguments: ## => Returns: 0 function crash() { - _bashutils_reset_environment [ "${1}" == "true" ] && CRASH_TERMINATE=true || CRASH_TERMINATE=false shift # shellcheck disable=SC2002 @@ -135,7 +109,6 @@ $(for index in $(seq 0 1000); do if [ -z "$(caller "${index}")" ]; then break; e $([ "${CRASH_TERMINATE}" == "false" ] && echo "!!! This crash will not terminate the shell !!!") EOF [ "${CRASH_TERMINATE}" == "true" ] && exit 69 - _bashutils_restore_environment } # Checks @@ -143,60 +116,60 @@ EOF ## => Checks whether the provided command name is an alias. ## => Arguments: ## => Returns: 0 if it is, 1 otherwise -function is_command_alias() { _bashutils_reset_environment; [[ "$(type -t "${1}")" == alias ]]; _bashutils_return_environment ${?}; } +function is_command_alias() { [[ "$(type -t "${1}")" == alias ]]; } ## => Checks whether the provided command name is a keyword. ## => Arguments: ## => Returns: 0 if it is, 1 otherwise -function is_command_keyword() { _bashutils_reset_environment; [[ "$(type -t "${1}")" == keyword ]]; _bashutils_return_environment ${?}; } +function is_command_keyword() { [[ "$(type -t "${1}")" == keyword ]]; } ## => Checks whether the provided command name is a function. ## => Arguments: ## => Returns: 0 if it is, 1 otherwise -function is_command_function() { _bashutils_reset_environment; [[ "$(type -t "${1}")" == function ]]; _bashutils_return_environment ${?}; } +function is_command_function() { [[ "$(type -t "${1}")" == function ]]; } ## => Checks whether the provided command name is a builtin. ## => Arguments: ## => Returns: 0 if it is, 1 otherwise -function is_command_builtin() { _bashutils_reset_environment; [[ "$(type -t "${1}")" == builtin ]]; _bashutils_return_environment ${?}; } +function is_command_builtin() { [[ "$(type -t "${1}")" == builtin ]]; } ## => Checks whether the provided command name is a file. ## => Arguments: ## => Returns: 0 if it is, 1 otherwise -function is_command_file() { _bashutils_reset_environment; [[ "$(type -t "${1}")" == file ]]; _bashutils_return_environment ${?}; } +function is_command_file() { [[ "$(type -t "${1}")" == file ]]; } ## => Checks whether the provided command name is defined. ## => Arguments: ## => Returns: 0 if it is, 1 otherwise -function is_command_defined() { _bashutils_reset_environment; [[ "$(type -t "${1}")" == "" ]]; _bashutils_return_environment ${?}; } +function is_command_defined() { [[ "$(type -t "${1}")" == "" ]]; } ## Input type ## => Checks whether the provided input is a bool. ## => Arguments: ## => Returns: 0 if it is, 1 otherwise -function is_input_bool() { _bashutils_reset_environment; [ "${1}" == "true" ] || [ "${1}" == "false" ]; _bashutils_return_environment "${?}"; } +function is_input_bool() { [ "${1}" == "true" ] || [ "${1}" == "false" ]; } ## => Checks whether the provided input is a byte. ## => Arguments: ## => Returns: 0 if it is, 1 otherwise -function is_input_byte() { _bashutils_reset_environment; [[ "${1}" =~ ^[0-9]+$ ]] && [[ "${1}" -gt -1 ]] && [["${1}" -lt 256 ]]; _bashutils_return_environment "${?}"; } +function is_input_byte() { [[ "${1}" =~ ^[0-9]+$ ]] && [[ "${1}" -gt -1 ]] && [[ "${1}" -lt 256 ]]; } ## => Checks whether the provided input is a integer/number. ## => Arguments: ## => Returns: 0 if it is, 1 otherwise -function is_input_int() { _bashutils_reset_environment; [[ "${1}" =~ ^[0-9]+$ ]]; _bashutils_return_environment "${?}"; } +function is_input_int() { [[ "${1}" =~ ^[0-9]+$ ]]; } ## => Checks whether the provided input is a char. ## => Arguments: ## => Returns: 0 if it is, 1 otherwise -function is_input_char() { _bashutils_reset_environment; [[ "${1}" =~ ^.$ ]]; _bashutils_return_environment "${?}"; } +function is_input_char() { [[ "${1}" =~ ^.$ ]]; } # Variable definition ## => Sets the specified variable to the specified value if it is undefined. ## => Arguments: ## => Returns: 0 if it is, 1 otherwise -function set_undefined() { _bashutils_reset_environment; [ -z "${!1}" ] && export "${1}=${2}"; _bashutils_restore_environment; } +function set_undefined() { [ -z "${!1}" ] && export "${1}=${2}"; } ## => Sets the specified variable to the specified value if it is defined. ## => Arguments: ## => Returns: 0 if it is, 1 otherwise -function set_defined() { _bashutils_reset_environment; [ -n "${!1}" ] && export "${1}=${2}"; _bashutils_restore_environment; } +function set_defined() { [ -n "${!1}" ] && export "${1}=${2}"; } # Initialize variables -unset _BASHUTILS_ENVIRONMENT_PREVIOUS_OPTIONS -unset _BASHUTILS_ENVIRONMENT_PREVIOUS_PIPEFAIL set_undefined "BASHUTILS_LOGLEVEL" "3" +set_undefined "BASHUTILS_LOGINDENTATION" ">" +set_undefined "BASHUTILS_SUBSHELL" "1" ################################ diff --git a/docs/docs/features/logging.md b/docs/docs/features/logging.md index a815bf3..4bad3f2 100644 --- a/docs/docs/features/logging.md +++ b/docs/docs/features/logging.md @@ -61,4 +61,6 @@ To trigger a crash, simply invoke `crash `. ## Customization bashutils allows you to customize the logging system via environment variables. ### `BASHUTILS_LOGLEVEL` -Set it to the desired minimum log level ID. Defaults to `3` (informational). +The desired minimum log level ID. Defaults to `3` (informational). +### `BASHUTILS_SUBSHELL` +Subshell identifier. Increase this variable by `1` each time you invoke another script. Defaults to `1` (**DO NOT LOWER THIS VALUE**). diff --git a/test.sh b/test.sh index d9f331f..c964541 100755 --- a/test.sh +++ b/test.sh @@ -31,224 +31,230 @@ check_input_char " # Utility methods +function bashutils() { + set +uo pipefail + source "bashutils.sh" + set -uo pipefail +} function invalid() { echo ":: Error: Invalid test ${TEST}, skipping"; } function fail() { - echo -e ":: Test ${TEST} failed during stage ${STAGE}\n ${*//\\n/\\n }" - [ "${TEST_FAILFAST:-true}" == "true" ] && exit 1 + echo -e ":: Test ${TEST} failed during stage ${STAGE}\n ${*//\\n/\\n }" + [ "${TEST_FAILFAST:-true}" == "true" ] && exit 1 } ## Assertions function assert_equals() { - EXPECTED="${1}" - ACTUAL="${2}" + EXPECTED="${1}" + ACTUAL="${2}" - [[ "${ACTUAL}" != "${EXPECTED}" ]] && fail "String does not match expected string\nExpected: '${EXPECTED}'\nActual: '${ACTUAL}'" + [[ "${ACTUAL}" != "${EXPECTED}" ]] && fail "String does not match expected string\nExpected: '${EXPECTED}'\nActual: '${ACTUAL}'" } function assert_contains() { - EXPECTED="${1}" - ACTUAL="${2}" + EXPECTED="${1}" + ACTUAL="${2}" - [[ "${ACTUAL}" != *"${EXPECTED}"* ]] && fail "String does not contain expected string\nExpected: '${EXPECTED}'\nActual: '${ACTUAL}'" - } + [[ "${ACTUAL}" != *"${EXPECTED}"* ]] && fail "String does not contain expected string\nExpected: '${EXPECTED}'\nActual: '${ACTUAL}'" +} # Test loop for TEST in ${TESTS}; do - [ -z "${TEST}" ] || [[ "${TEST}" == "#"* ]] && continue + [ -z "${TEST}" ] || [[ "${TEST}" == "#"* ]] && continue - echo ":: Running test ${TEST}" - export "TEST" - export "STAGE=0" + echo ":: Running test ${TEST}" + export "TEST" + export "STAGE=0" - # Logging - if [[ "${TEST}" == "logger_"* ]]; then - if [ "${TEST}" == "logger_crash" ]; then - assert_contains "It crashed!" "$( ( - source "bashutils.sh" - crash "true" "It crashed!" - ) &> /dev/stdout)" - else - assert_contains "This is a test message" "$( ( - source "bashutils.sh" - # shellcheck disable=SC2034 - BASHUTILS_LOGLEVEL=0 - ${TEST//logger_/} "This is a test message" - ) &> /dev/stdout)" + # Logging + if [[ "${TEST}" == "logger_"* ]]; then + if [ "${TEST}" == "logger_crash" ]; then + assert_contains "It crashed!" "$( ( + bashutils + crash "true" "It crashed!" + ) &>/dev/stdout)" + else + assert_contains "This is a test message" "$( ( + bashutils + # shellcheck disable=SC2034 + BASHUTILS_LOGLEVEL=0 + ${TEST//logger_/} "This is a test message" + ) &>/dev/stdout)" - fi + fi - # Checks - elif [[ "${TEST}" == "check_"* ]]; then - # Command checks - if [[ "${TEST}" == "check_command_"* ]]; then - case "${TEST//check_command_/}" in - "alias") - assert_equals "0" "$( - source "bashutils.sh" - alias "verynicealias=ls" - is_command_alias "verynicealias" - echo "${?}" - )" - ;; - "keyword") - assert_equals "0" "$( - source "bashutils.sh" - is_command_keyword "continue" - echo "${?}" - )" - ;; - "function") - assert_equals "0" "$( - source "bashutils.sh" - is_command_function "is_command_function" - echo "${?}" - )" - ;; - "builtin") - assert_equals "0" "$( - source "bashutils.sh" - is_command_builtin "set" - echo "${?}" - )" - ;; - "file") - assert_equals "0" "$( - source "bashutils.sh" - is_command_file "clear" - echo "${?}" - )" - ;; - "defined") - assert_equals "0" "$( - source "bashutils.sh" - is_command_defined "echo" - echo "${?}" - )" - ;; - *) - invalid - ;; - esac + # Checks + elif [[ "${TEST}" == "check_"* ]]; then + # Command checks + if [[ "${TEST}" == "check_command_"* ]]; then + case "${TEST//check_command_/}" in + "alias") + assert_equals "0" "$( + bashutils + alias "verynicealias=ls" + is_command_alias "verynicealias" + echo "!$(type -t "verynicealias")!" + #echo -n "${?}" + )" + ;; + "keyword") + assert_equals "0" "$( + bashutils + is_command_keyword "continue" + echo "${?}" + )" + ;; + "function") + assert_equals "0" "$( + bashutils + is_command_function "is_command_function" + echo "${?}" + )" + ;; + "builtin") + assert_equals "0" "$( + bashutils + is_command_builtin "set" + echo "${?}" + )" + ;; + "file") + assert_equals "0" "$( + bashutils + is_command_file "clear" + echo "${?}" + )" + ;; + "defined") + assert_equals "0" "$( + bashutils + is_command_defined "echo" + echo "${?}" + )" + ;; + *) + invalid + ;; + esac - # Input checks - elif [[ "${TEST}" == "check_input_"* ]]; then - case "${TEST//check_input_/}" in - "bool") - assert_equals "0" "$( - source "bashutils.sh" - is_input_bool "true" - echo "${?}" - )" - export "STAGE=1" - assert_equals "0" "$( - source "bashutils.sh" - is_input_bool "false" - echo "${?}" - )" - export "STAGE=2" - assert_equals "1" "$( - source "bashutils.sh" - is_input_bool "0" - echo "${?}" - )" - export "STAGE=3" - assert_equals "1" "$( - source "bashutils.sh" - is_input_bool "1" - echo "${?}" - )" - ;; - "byte") - assert_equals "0" "$( - source "bashutils.sh" - is_input_byte "0" - echo "${?}" - )" - export "STAGE=1" - assert_equals "0" "$( - source "bashutils.sh" - is_input_byte "255" - echo "${?}" - )" - export "STAGE=2" - assert_equals "0" "$( - source "bashutils.sh" - is_input_byte "69" - echo "${?}" - )" - export "STAGE=3" - assert_equals "1" "$( - source "bashutils.sh" - is_input_byte "256" - echo "${?}" - )" - export "STAGE=4" - assert_equals "1" "$( - source "bashutils.sh" - is_input_byte "-1" - echo "${?}" - )" - export "STAGE=5" - assert_equals "1" "$( - source "bashutils.sh" - is_input_int "asd" - echo "${?}" - )" - ;; - "int") - assert_equals "0" "$( - source "bashutils.sh" - is_input_int "9223372036854775807" - echo "${?}" - )" - export "STAGE=1" - assert_equals "1" "$( - source "bashutils.sh" - is_input_int "asd" - echo "${?}" - )" - ;; - "char") - assert_equals "0" "$( - source "bashutils.sh" - is_input_int "!" - echo "${?}" - )" - export "STAGE=1" - assert_equals "0" "$( - source "bashutils.sh" - is_input_int "™" - echo "${?}" - )" - export "STAGE=2" - assert_equals "0" "$( - source "bashutils.sh" - is_input_int "5" - echo "${?}" - )" - export "STAGE=3" - assert_equals "1" "$( - source "bashutils.sh" - is_input_int "asd" - echo "${?}" - )" - export "STAGE=4" - assert_equals "1" "$( - source "bashutils.sh" - is_input_int "666" - echo "${?}" - )" - ;; - *) - invalid - ;; - esac + # Input checks + elif [[ "${TEST}" == "check_input_"* ]]; then + case "${TEST//check_input_/}" in + "bool") + assert_equals "0" "$( + bashutils + is_input_bool "true" + echo "${?}" + )" + export "STAGE=1" + assert_equals "0" "$( + bashutils + is_input_bool "false" + echo "${?}" + )" + export "STAGE=2" + assert_equals "1" "$( + bashutils + is_input_bool "0" + echo "${?}" + )" + export "STAGE=3" + assert_equals "1" "$( + bashutils + is_input_bool "1" + echo "${?}" + )" + ;; + "byte") + assert_equals "0" "$( + bashutils + is_input_byte "0" + echo "${?}" + )" + export "STAGE=1" + assert_equals "0" "$( + bashutils + is_input_byte "255" + echo "${?}" + )" + export "STAGE=2" + assert_equals "0" "$( + bashutils + is_input_byte "69" + echo "${?}" + )" + export "STAGE=3" + assert_equals "1" "$( + bashutils + is_input_byte "256" + echo "${?}" + )" + export "STAGE=4" + assert_equals "1" "$( + bashutils + is_input_byte "-1" + echo "${?}" + )" + export "STAGE=5" + assert_equals "1" "$( + bashutils + is_input_int "asd" + echo "${?}" + )" + ;; + "int") + assert_equals "0" "$( + bashutils + is_input_int "9223372036854775807" + echo "${?}" + )" + export "STAGE=1" + assert_equals "1" "$( + bashutils + is_input_int "asd" + echo "${?}" + )" + ;; + "char") + assert_equals "0" "$( + bashutils + is_input_int "!" + echo "${?}" + )" + export "STAGE=1" + assert_equals "0" "$( + bashutils + is_input_int "™" + echo "${?}" + )" + export "STAGE=2" + assert_equals "0" "$( + bashutils + is_input_int "5" + echo "${?}" + )" + export "STAGE=3" + assert_equals "1" "$( + bashutils + is_input_int "asd" + echo "${?}" + )" + export "STAGE=4" + assert_equals "1" "$( + bashutils + is_input_int "666" + echo "${?}" + )" + ;; + *) + invalid + ;; + esac + + # Invalid command + else + invalid + fi # Invalid command else - invalid + invalid fi - - # Invalid command - else - invalid - fi done