Initial commit
This commit is contained in:
parent
773ab23f22
commit
7dfd4a8cd5
2
.gitignore
vendored
2
.gitignore
vendored
@ -161,3 +161,5 @@ Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
g.log
|
||||
g.log*
|
||||
|
17
example.bash
Executable file
17
example.bash
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
source src/g.bash
|
||||
|
||||
g::log::enable file g.log
|
||||
g::log::setLevel internal
|
||||
|
||||
g::info "Starting try-catch..."
|
||||
|
||||
try {
|
||||
ls -lah baddir
|
||||
} catch {
|
||||
declare local error="$?"
|
||||
g::error "Error code: $error"
|
||||
}
|
||||
|
||||
g::info "After try-catch..."
|
650
src/g.bash
Normal file
650
src/g.bash
Normal file
@ -0,0 +1,650 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
set -u
|
||||
set +o histexpand
|
||||
shopt -qs failglob
|
||||
shopt -s expand_aliases
|
||||
|
||||
## ====================== GLOBALS ====================== ##
|
||||
export g__EXISTS=true
|
||||
|
||||
# The global logging level. Lower the number, higher the priority.
|
||||
g__LOGGING_LEVEL=2
|
||||
|
||||
# True if we should log messages to stdout
|
||||
g__LOGGING_TO_STDOUT=true
|
||||
|
||||
# True if we should log messages to stderr
|
||||
g__LOGGING_TO_STDERR=false
|
||||
|
||||
# True if we should log messages to a file
|
||||
g__LOGGING_TO_FILE=false
|
||||
|
||||
# If file logging is enabled, the absolute path to the file
|
||||
g__LOGGING_FILE='g.log'
|
||||
|
||||
|
||||
|
||||
|
||||
## ====================== UTILITIES ====================== ##
|
||||
|
||||
#
|
||||
# Check if an input value is numeric. (Supports decimals.)
|
||||
#
|
||||
# @param value - the value to check
|
||||
# @return 0 if numeric, 1 otherwise
|
||||
#
|
||||
function g::util::isNumeric() {
|
||||
local value="$1"
|
||||
|
||||
local rex='^[+-]?[0-9]+([.][0-9]+)?$'
|
||||
|
||||
if [[ "$value" =~ $rex ]]; then
|
||||
return 0 # true
|
||||
else
|
||||
return 1 # false
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Returns true if the given array contains a value.
|
||||
#
|
||||
# e.g. `g::arr::includes 'value' "${array[@]}"`
|
||||
#
|
||||
# @param value - the value to search for
|
||||
# @param array - the array, by value
|
||||
# @return 0 if included, 1 otherwise
|
||||
#
|
||||
function g::arr::includes() {
|
||||
local value="$1"
|
||||
shift
|
||||
local array=("$@")
|
||||
|
||||
for element in "${array[@]}"; do
|
||||
if [[ $element == "$value" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
#
|
||||
# Canonical true return value.
|
||||
# @return true
|
||||
#
|
||||
function g::util::true() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#
|
||||
# Canonical false return value.
|
||||
# @return false
|
||||
#
|
||||
function g::util::false() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#
|
||||
# Cast the return code of the command to a boolean echo output.
|
||||
#
|
||||
# @param ...cmd - the command to execute
|
||||
# @echo 1 if return is true, 0 otherwise
|
||||
#
|
||||
function g::util::returnToEcho() {
|
||||
local cmd="$@"
|
||||
|
||||
if eval "$cmd"; then
|
||||
echo 1
|
||||
else
|
||||
echo 0
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Append the input string to a given file.
|
||||
#
|
||||
# @param filename
|
||||
# @param string
|
||||
#
|
||||
function g::file::appendString() {
|
||||
local filename="$1"
|
||||
local string="$2"
|
||||
|
||||
echo "$string" >> "$filename"
|
||||
}
|
||||
|
||||
function g::file::exists() {
|
||||
local filename="$1"
|
||||
|
||||
if [[ -f "$filename" ]]; then
|
||||
return $(g::util::true)
|
||||
else
|
||||
return $(g::util::false)
|
||||
fi
|
||||
}
|
||||
|
||||
function g::file::directoryExists() {
|
||||
local filename="$1"
|
||||
|
||||
if [[ -d "$filename" ]]; then
|
||||
return $(g::util::true)
|
||||
else
|
||||
return $(g::util::false)
|
||||
fi
|
||||
}
|
||||
|
||||
function g::file::touch() {
|
||||
local filename="$1"
|
||||
echo '' >> "$filename"
|
||||
}
|
||||
|
||||
function g::file::truncate() {
|
||||
local filename="$1"
|
||||
if g::file::exists "$filename"; then
|
||||
echo '' > "$filename"
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Throw a program error. Exits.
|
||||
# @todo move to g::err
|
||||
#
|
||||
function g::error::throw() {
|
||||
local message="$1"
|
||||
echo "$message" 1>&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
#
|
||||
# Get the current date in UTC format.
|
||||
#
|
||||
# @echo the date string
|
||||
#
|
||||
function g::now() {
|
||||
date -u +"%Y-%m-%dT%H:%M:%SZ"
|
||||
}
|
||||
|
||||
#
|
||||
# Get a stack trace.
|
||||
#
|
||||
# @param message - optional message to include
|
||||
# @echo the stack trace
|
||||
#
|
||||
function g::util::trace() {
|
||||
local stack=""
|
||||
local i message="${1:-""}"
|
||||
local stack_size=${#FUNCNAME[@]}
|
||||
|
||||
# to avoid noise we start with 1 to skip the trace function
|
||||
for (( i=1; i<$stack_size; i++ )); do
|
||||
local func="${FUNCNAME[$i]}"
|
||||
|
||||
[ x$func = x ] && func=MAIN
|
||||
|
||||
local linen="${BASH_LINENO[$(( i - 1 ))]}"
|
||||
local src="${BASH_SOURCE[$i]}"
|
||||
|
||||
[ x"$src" = x ] && src=non_file_source
|
||||
|
||||
stack+=$'\n'" at: "$func" "$src" "$linen
|
||||
done
|
||||
|
||||
stack="${message}${stack}"
|
||||
echo "${stack}"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
## ====================== STRINGS ====================== ##
|
||||
|
||||
function g::str::quote() {
|
||||
local string="$1"
|
||||
echo "'$string'"
|
||||
}
|
||||
|
||||
function g::str::quote::eval() {
|
||||
local cmd="$@"
|
||||
local str="$(eval "$cmd")"
|
||||
echo "$(g::str::quote "$str")"
|
||||
}
|
||||
|
||||
function g::str::length() {
|
||||
local string="$1"
|
||||
echo "$(expr length "$string")"
|
||||
}
|
||||
|
||||
function g::str::padLeft() {
|
||||
local string="$1"
|
||||
local padToLength="$2"
|
||||
local padCharacter="${3:- }"
|
||||
|
||||
local length="$(expr length "$string")"
|
||||
|
||||
while [[ "$length" -lt "$padToLength" ]]; do
|
||||
string="${padCharacter}${string}"
|
||||
length="$(expr length "$string")"
|
||||
done
|
||||
|
||||
echo "$string"
|
||||
}
|
||||
|
||||
function g::str::padRight() {
|
||||
local string="$1"
|
||||
local padToLength="$2"
|
||||
local padCharacter="${3:- }"
|
||||
|
||||
local length="$(expr length "$string")"
|
||||
|
||||
while [[ "$length" -lt "$padToLength" ]]; do
|
||||
string="${string}${padCharacter}"
|
||||
length="$(expr length "$string")"
|
||||
done
|
||||
|
||||
echo "$string"
|
||||
}
|
||||
|
||||
function g::str::padCenter() {
|
||||
local string="$1"
|
||||
local padToLength="$2"
|
||||
local padCharacter="${3:- }"
|
||||
|
||||
local length="$(expr length "$string")"
|
||||
local leftSide=1
|
||||
|
||||
while [[ "$length" -lt "$padToLength" ]]; do
|
||||
if [[ "$leftSide" -eq "1" ]]; then
|
||||
string="${padCharacter}${string}"
|
||||
leftSide=0
|
||||
else
|
||||
string="${string}${padCharacter}"
|
||||
leftSide=1
|
||||
fi
|
||||
|
||||
length="$(expr length "$string")"
|
||||
done
|
||||
|
||||
echo "$string"
|
||||
}
|
||||
|
||||
function g::str::substring() {
|
||||
local string="$1"
|
||||
local startAt="$2"
|
||||
local length="$3"
|
||||
|
||||
echo "${string:$startAt:$length}"
|
||||
}
|
||||
|
||||
function g::str::reverse() {
|
||||
local input="$1"
|
||||
|
||||
local output=""
|
||||
local length="$(g::str::length "$input")"
|
||||
|
||||
for (( i=$length; i>=0; i-- )); do
|
||||
output="${output}${input:$i:1}"
|
||||
done
|
||||
|
||||
echo "${output}"
|
||||
}
|
||||
|
||||
function g::str::startsWith() {
|
||||
local string="$1"
|
||||
local startsWith="$2"
|
||||
|
||||
local startsLength="$(g::str::length "$startsWith")"
|
||||
local substring="$(g::str::substring "$string" 0 $startsLength)"
|
||||
|
||||
if [[ "$substring" == "$startsWith" ]]; then
|
||||
return $(g::util::true)
|
||||
else
|
||||
return $(g::util::false)
|
||||
fi
|
||||
}
|
||||
|
||||
function g::str::endsWith() {
|
||||
local string="$1"
|
||||
local endsWith="$2"
|
||||
|
||||
local revString="$(g::str::reverse "$string")"
|
||||
local revEndsWith="$(g::str::reverse "$endsWith")"
|
||||
|
||||
if g::str::startsWith "$revString" "$revEndsWith"; then
|
||||
return $(g::util::true)
|
||||
else
|
||||
return $(g::util::false)
|
||||
fi
|
||||
}
|
||||
|
||||
# string helpers
|
||||
# string split
|
||||
|
||||
|
||||
|
||||
|
||||
## ====================== LOGGING ====================== ##
|
||||
|
||||
# output functions and logging
|
||||
# capture output
|
||||
# silence output
|
||||
# colors
|
||||
# powerline
|
||||
# tables
|
||||
# width-fill
|
||||
|
||||
#
|
||||
# Convert a string-form logging level to its numeric form.
|
||||
#
|
||||
# @param name
|
||||
# @echo the numeric form
|
||||
#
|
||||
function g::log::stringNameToNumber() {
|
||||
local name="$1"
|
||||
|
||||
if [ "$name" = "error" ]; then
|
||||
echo 1
|
||||
elif [ "$name" = "warn" ]; then
|
||||
echo 2
|
||||
elif [ "$name" = "info" ]; then
|
||||
echo 3
|
||||
elif [ "$name" = "debug" ]; then
|
||||
echo 4
|
||||
elif [ "$name" = "verbose" ]; then
|
||||
echo 5
|
||||
elif [ "$name" = "internal" ]; then
|
||||
echo 6
|
||||
else
|
||||
echo 0
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Convert the numeric logging level to its string name.
|
||||
#
|
||||
# @param level
|
||||
# @echo the string name
|
||||
#
|
||||
function g::log::numberToStringName() {
|
||||
local level="$1"
|
||||
|
||||
if [ "$level" = "1" ]; then
|
||||
echo error
|
||||
elif [ "$level" = "2" ]; then
|
||||
echo warn
|
||||
elif [ "$level" = "3" ]; then
|
||||
echo info
|
||||
elif [ "$level" = "4" ]; then
|
||||
echo debug
|
||||
elif [ "$level" = "5" ]; then
|
||||
echo verbose
|
||||
elif [ "$level" = "6" ]; then
|
||||
echo internal
|
||||
else
|
||||
echo none
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Normalize a value that may be either a string- or numeric-form
|
||||
# logging level to a numeric form.
|
||||
#
|
||||
# @param level
|
||||
# @echo the numeric form
|
||||
#
|
||||
function g::log::normalizeLevelToNumber() {
|
||||
local level="$1"
|
||||
|
||||
if g::util::isNumeric "$level"; then
|
||||
echo $level
|
||||
else
|
||||
echo $(g::log::stringNameToNumber $level)
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Get the current logging level, as a string.
|
||||
#
|
||||
# @echo the string name
|
||||
#
|
||||
function g::log::getLevel() {
|
||||
echo $(g::log::numberToStringName $g__LOGGING_LEVEL)
|
||||
}
|
||||
|
||||
#
|
||||
# Set the logging level by string name.
|
||||
#
|
||||
# @param name
|
||||
#
|
||||
function g::log::setLevel() {
|
||||
local name="$1"
|
||||
|
||||
local level=$(g::log::stringNameToNumber "$name")
|
||||
|
||||
g__LOGGING_LEVEL=$level
|
||||
}
|
||||
|
||||
#
|
||||
# Enable a particular logging format.
|
||||
#
|
||||
# @param target - stdout | stderr | file
|
||||
# @param param - if target is "file", the path of the file to log to
|
||||
# @throws if the target is invalid
|
||||
#
|
||||
function g::log::enable() {
|
||||
local target="$1"
|
||||
local param="$2"
|
||||
|
||||
if [[ $target == 'stdout' ]]; then
|
||||
g__LOGGING_TO_STDOUT=true
|
||||
elif [[ $target == 'stderr' ]]; then
|
||||
g__LOGGING_TO_STDERR=true
|
||||
elif [[ $target == 'file' ]]; then
|
||||
g__LOGGING_TO_FILE=true
|
||||
g__LOGGING_FILE="$(realpath "${param:-$g__LOGGING_FILE}")"
|
||||
|
||||
g::file::appendString "$g__LOGGING_FILE" "$(g::log::format 3 "====== Logging started. ======")"
|
||||
else
|
||||
g::error::throw "Invalid logging target: ${target}"
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Disable a particular logging format.
|
||||
#
|
||||
# @param target - stdout | stderr | file
|
||||
# @throws if the target is invalid
|
||||
#
|
||||
function g::log::disable() {
|
||||
local target="$1"
|
||||
|
||||
if [[ $target == 'stdout' ]]; then
|
||||
g__LOGGING_TO_STDOUT=false
|
||||
elif [[ $target == 'stderr' ]]; then
|
||||
g__LOGGING_TO_STDERR=false
|
||||
elif [[ $target == 'file' ]]; then
|
||||
g__LOGGING_TO_FILE=false
|
||||
else
|
||||
g::error::throw "Invalid logging target: ${target}"
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Write to standard output.
|
||||
#
|
||||
# @param output
|
||||
#
|
||||
function g::log::stdout() {
|
||||
local output="$1"
|
||||
echo "$output"
|
||||
}
|
||||
|
||||
#
|
||||
# Write to standard error.
|
||||
#
|
||||
# @param output
|
||||
#
|
||||
function g::log::stderr() {
|
||||
local output="$1"
|
||||
echo "$output" 1>&2
|
||||
}
|
||||
|
||||
#
|
||||
# Write to the log, on all configured formats.
|
||||
#
|
||||
# @param output
|
||||
#
|
||||
function g::log::write() {
|
||||
local output="$1"
|
||||
|
||||
if $g__LOGGING_TO_STDOUT; then
|
||||
g::log::stdout "$output"
|
||||
fi
|
||||
|
||||
if $g__LOGGING_TO_STDERR; then
|
||||
g::log::stderr "$output"
|
||||
fi
|
||||
|
||||
if $g__LOGGING_TO_FILE; then
|
||||
g::file::appendString "$g__LOGGING_FILE" "$output"
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Format an output string with logging information like log level and date.
|
||||
#
|
||||
# @param level - the numeric logging level
|
||||
# @param output
|
||||
#
|
||||
function g::log::format() {
|
||||
local level="$1"
|
||||
local output="$2"
|
||||
|
||||
local levelDisplay="[$(g::log::numberToStringName $level)]"
|
||||
local paddedLevelDisplay="$(g::str::padLeft "$levelDisplay" 10)"
|
||||
echo "$paddedLevelDisplay [$(g::now)] $output"
|
||||
}
|
||||
|
||||
#
|
||||
# Log a message.
|
||||
#
|
||||
# @param inputLevel
|
||||
# @param output
|
||||
#
|
||||
function g::log() {
|
||||
local inputLevel="$1"
|
||||
local output="$2"
|
||||
|
||||
local level="$(g::log::normalizeLevelToNumber $inputLevel)"
|
||||
|
||||
if [ $level -le $g__LOGGING_LEVEL ]; then
|
||||
g::log::write "$(g::log::format $level "$output")"
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Log an error message.
|
||||
#
|
||||
# @param output
|
||||
#
|
||||
function g::error() {
|
||||
local output="$1"
|
||||
g::log error "$output"
|
||||
}
|
||||
|
||||
#
|
||||
# Log a warning message.
|
||||
#
|
||||
# @param output
|
||||
#
|
||||
function g::warn() {
|
||||
local output="$1"
|
||||
g::log warn "$output"
|
||||
}
|
||||
|
||||
#
|
||||
# Log an informational message.
|
||||
#
|
||||
# @param output
|
||||
#
|
||||
function g::info() {
|
||||
local output="$1"
|
||||
g::log info "$output"
|
||||
}
|
||||
|
||||
#
|
||||
# Log a debugging message.
|
||||
#
|
||||
# @param output
|
||||
#
|
||||
function g::debug() {
|
||||
local output="$1"
|
||||
g::log debug "$output"
|
||||
}
|
||||
|
||||
#
|
||||
# Log a verbose message.
|
||||
#
|
||||
# @param output
|
||||
#
|
||||
function g::internal() {
|
||||
local output="$1"
|
||||
g::log verbose "$output"
|
||||
}
|
||||
|
||||
#
|
||||
# Log a verbose message.
|
||||
#
|
||||
# @param output
|
||||
#
|
||||
function g::internal() {
|
||||
local output="$1"
|
||||
g::log internal "$output"
|
||||
}
|
||||
|
||||
function g::log::rotate() {
|
||||
local logfile="$g__LOGGING_FILE"
|
||||
if g::file::exists "$logfile"; then
|
||||
cp "$logfile" "${logfile}-$(g::now)"
|
||||
g::file::truncate "$logfile"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
## ====================== ERROR HANDLING ====================== ##
|
||||
|
||||
declare -ig g__INSIDE_TRY_CATCH=0
|
||||
declare -g g__PRESET_SHELL_OPTS="$-"
|
||||
|
||||
alias try='[[ $g__INSIDE_TRY_CATCH -eq 0 ]] || g__PRESET_SHELL_OPTS="$(echo $- | sed 's/[is]//g')"; g__INSIDE_TRY_CATCH+=1; set +e; ( set -e; true; '
|
||||
alias catch='); declare g__TRY_RESULT=$?; g__INSIDE_TRY_CATCH+=-1; [[ $g__INSIDE_TRY_CATCH -lt 1 ]] || set -${g__PRESET_SHELL_OPTS:-e} && g::err::parse $g__TRY_RESULT || '
|
||||
|
||||
function g::err::parse() {
|
||||
local tryCode="$1"
|
||||
unset g__TRY_RESULT;
|
||||
|
||||
if [[ $tryCode -gt 0 ]]; then
|
||||
local IFS=$'\n'
|
||||
return $tryCode
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# math helpers
|
||||
|
||||
# dependency declarations
|
||||
# package/script imports & singleton requires
|
||||
|
||||
# output helpers
|
||||
|
||||
# argument parsing
|
||||
|
||||
# try-catch & exception handling
|
||||
|
||||
# named parameters & arrays/maps
|
||||
|
||||
# JSON parsing, INI parsing, rich data types
|
Loading…
Reference in New Issue
Block a user