diff --git a/modules/scripting/array.mod b/modules/scripting/array.mod new file mode 100644 index 0000000..5a86cf1 --- /dev/null +++ b/modules/scripting/array.mod @@ -0,0 +1,218 @@ +#!/bin/bash +# +# Author: Hugo Thunnissen +# License: see LICENSE file. +# +## +# Utility functions for common array operations. +# To prevent circular references, none of the functions allow +# usage on an array named "array", and some won't allow the usage +# of an array by the name "oth_array" for a second arrayname that is passed. +# Functions that do variable assignment won't allow the usage of a variable +# by the name "variable". +# +## + +## IMPPORTANT: CANNOT DO THIS IN A SUBSHELL i.e. $( ) + +## +# Check if an array is eligible to use the functions on. +# return values: +# 1: Array variable has not been set. +# 2: The array variable is named "array" +# + + +# array, string, delimiter +function String::split() { + #!/bin/bash +Array::isValid "$1" || return $? +[[ $2 ]] || return 1 +delimiter=${3:-" "} +s=$2$delimiter +# echo del: $delimiter +# echo string: $s +declare -n array="$1" +while [[ $s ]]; do + array+=( "${s%%"$delimiter"*}" ); + # echo added element now: ${array[@]} + s=${s#*"$delimiter"}; + # echo remains: $s +done; +} + +function Array::isValid() { + if ! declare -p "$1" &>>/dev/null; then + echo 'Array: Array variable needs to be set.' >&2 + return 1 + elif [[ $1 == array ]]; then + echo 'Array: Array variable can not be named "array"' >&2 + return 2 + fi +} + +## +# Check if a variable can be assigned. +# return values: +# 3: The variable has not yet been declared +# 5: The variable is named "variable" +# +# $1 variable_name +function Array::varIsValid() { + if ! [[ -v $1 ]]; then + echo 'Array: Variable needs to be declared before assigning to it.' >&2 + return 3 + elif [[ $1 == variable ]]; then + echo 'Array: Variable cannot be named "variable"' >&2 + return 5 + fi +} + +## +# pop an array +# return values: +# 4: array size is 0 +# other: see Array::isValid +# +# $1: array_name +function Array::pop() { + Array::isValid "$1" || return $? + + declare -n array="$1" + [[ ${#array[@]} -gt 0 ]] || return 4 + + unset 'array[-1]' +} + +## +# Pop an array and assign the popped value to a variable +# return values: +# 4: array size is 0 +# other: see Array::isValid and Array::varIsValid +# +# $1: array_name +# $2: variable_name +function Array::popToVar() { + Array::isValid "$1" || return $? + Array::varIsValid "$2" || return $? + + declare -n array="$1" variable="$2" + [[ ${#array[@]} -gt 0 ]] || return 4 + variable="${array[-1]}" + + Array::pop "$1" +} + +## +# Shift an array. +# $1: array_name +function Array::shift() { + Array::isValid "$1" || return $? + + declare -n array="$1" + declare -i shift_amount="$2" + ((shift_amount = shift_amount == 0 ? ++shift_amount : shift_amount)) + [[ ${#array[@]} -gt 0 ]] || return 4 + set -- "${array[@]}" + shift $shift_amount + + array=("$@") +} + +## +# Shift an array and assign the shifted value to a variable +# $1: array_name +# $2: variable_name +function Array::shiftToVar() { + Array::isValid "$1" || return $? + Array::varIsValid "$2" || return $? + + # TODO: Add shifting to multiple variables + declare -n array="$1" variable="$2" + [[ ${#array[@]} -gt 0 ]] || return 4 + variable="${array[0]}" + + Array::shift "$1" +} + +## +# Push a value to an array +# $1: array_name +# $2: value +function Array::push() { + Array::isValid "$1" || return $? + declare -n array="$1" + shift + + array=("${array[@]}" "$@") +} + +## +# Unshift a value to an array +# $1: array_name +# $*: values +function Array::unshift() { + Array::isValid "$1" || return $? + declare -n array="$1" + shift + + array=("$@" "${array[@]}") +} + +## +# Map through an array with a callback and assign the yielded values +# to the defined array. +# $1: array_name +# $2: array_to_be_assigned_to_name (may not be "oth_array") +# $3: callback_name +function Array::map() { + if ! declare -F "$3" &>>/dev/null; then + echo 'Array: Error, $3 for Array::map must be a function name' + return 6 + fi + Array::isValid "$1" || return $? + Array::isValid "$2" || return $? + + declare -n array="$1" + declare -a oth_array=() + declare func="$3" in_Array_map="true" + + for item in "${array[@]}"; do + "$func" "$item" + done + + declare -n array="$2" + + array=("${oth_array[@]}") +} + +## +# Strictly for use in Array::map callbacks. +# $1: Variable that should be added to the new array +function Array::yield() { + [[ "$in_Array_map" == true ]] && oth_array=("${oth_array[@]}" "$@") +} + +## +# Check if an array has a certain value stored in it. +# $1: arrayname +# $2: value +function Array::hasValue() { + if ! Array::isValid "$1"; then + echo "$(caller): $1 is not a valid array." + exit 1 + fi + + declare -n array="$1" + + if [[ $2 == +([0-9]) ]]; then + for item in "${array[@]}"; do + [[ $2 -eq $item ]] && return 0 + done + else + for item in "${array[@]}"; do + [[ "$2" == "$item" ]] && return 0 + done + fi + return 1 +} \ No newline at end of file diff --git a/modules/utility/bundle.sh b/modules/scripting/bundle.sh similarity index 100% rename from modules/utility/bundle.sh rename to modules/scripting/bundle.sh diff --git a/modules/utility/confirm.sh b/modules/scripting/confirm.func similarity index 69% rename from modules/utility/confirm.sh rename to modules/scripting/confirm.func index 2653676..3f747b4 100644 --- a/modules/utility/confirm.sh +++ b/modules/scripting/confirm.func @@ -1,10 +1,7 @@ +#!/bin/bash # ====================================================================== -# -# @link http://wuhrr.wordpress.com/2010/01/13/adding-confirmation-to-bash/#comment-3540 -# -# Function: confirm -# Asks the user to confirm an action, If the user does not answer yes, -# then the script will immediately exit. +# +# flags: -s, use strong confirmation, only yes or YES or Yes is accepted # # Parameters: # $@ - The confirmation message @@ -13,6 +10,10 @@ # > # Example 1 # > # The preferred way to use confirm # > confirm Delete file1? && echo rm file1 +# > or +# > [[ confirm Delete file1 ]]; then +# > rm file1 +# > fi # > # > # Example 2 # > # Use the $? variable to examine confirm's return value @@ -32,9 +33,12 @@ function confirm() { - echo -n "$@ " + local res="y Y yes YES Yes Sure sure SURE OK ok Ok" + local strong="yes YES Yes" + [[ $1 = "-s" ]] && { res=$strong; strong=true shift; } || strong="" + echo -n "$@" "$([[ $strong ]] && echo \< $res \>) ? " read -e answer - for response in y Y yes YES Yes Sure sure SURE OK ok Ok + for response in $res do if [ "_$answer" == "_$response" ] then diff --git a/modules/utility/debug.lib b/modules/scripting/debug.lib similarity index 100% rename from modules/utility/debug.lib rename to modules/scripting/debug.lib diff --git a/modules/scripting/helpers.mod b/modules/scripting/helpers.mod new file mode 100644 index 0000000..59ea814 --- /dev/null +++ b/modules/scripting/helpers.mod @@ -0,0 +1,62 @@ +#!/bin/bash +# must be json as a string, depends on jq +get_prop_value () { + local value + # echo in $1 get $2 + value=$(echo $1 | jq -r .$2) + echo $value +} + +is_array() { + local variable_name=$1 + [[ "$(declare -p $variable_name 2>/dev/null)" =~ "declare -a" ]] +} + +filename() { + # passed entire path + echo $(basename "$1" | rev | cut -f 2- -d '.' | rev) +} + +# // TODO remove and use path module +# // must change acl.lib and loginout, chromium, and ungoogled install files +adirname() { + # passed entire path + echo "$(cd "$(dirname "$1")" >/dev/null 2>&1 ; pwd -P )" +} + +user_exists() { + id -u $1 > /dev/null 2>&1 + [[ $? == 1 ]] || echo $1 + } + + +chmod_dirs() { + # passed entire path + local usesudo + [[ $1 == -s ]] && usesudo="sudo" && shift 2 + $usesudo find $1 -type f -exec chmod $2 {} + +} + +parse_option () { + # usage: parse_option -f "-b one -p 22 -F another" -p + # if -f is used then it will return the complete option + # otherwise just the options value + local opts;local f;local opt; local ret + [[ $1 = "-f" ]] && { f=true;shift 1; } + [[ $1 && $2 ]] || return 1 + opts=$1 + opt=$2 + [[ ! $opts =~ "$opt" ]] && return 1 + ret=$(sed -n "/^.*$opt\s\+\(\w\+\).*$/s//\1/p" <<< $opts) + [[ $f ]] && echo "$2 $ret" || echo $ret +} + +remove_end_spaces () { + del=${2:-\'} + # echo delimiter: $del + # sed -e "s/[[:space:]]\{1,\}$del/$del/" <<< "$1" + res=$(sed -e "s/^$del[[:space:]]*/$del/" <<< "$1") + # echo leading: $res + sed -e "s/[[:space:]]*${del}$/$del/" <<< "$res" +} + diff --git a/modules/scripting/minimize.func b/modules/scripting/minimize.func new file mode 100644 index 0000000..add532f --- /dev/null +++ b/modules/scripting/minimize.func @@ -0,0 +1,45 @@ +#!/bin/bash + +minimize () { + # will minimize script, removing comment lines and blank lines and inline comments + # TODO -r remove shebang, replace newlines with ; remove continuation \ + # usage: "source" (source as text or source as filename, as text MUST be quoted!) + # options: + # -t # argment will get text of source, otherwise it is the file path + # -o filepath # output to file + # -v # verbose, only applies when using -o. will output to file and return (stdout). + local out; local verbose; local text + local min="/^[[:space:]]*#/d; /#$/d; /^$/d; s/\(.*\)#.*/\1/" + local OPTION; local OPTARG; local OPTIND +# host=$(sed 's/\(.*\):.*/\1/' <<<"$SRC") + while getopts 'to:v' OPTION; do + # echo $OPTION $OPTARG + case "$OPTION" in + + t) + text=true + ;; + v) + verbose=true + ;; + o) + out="$OPTARG" + ;; + *) + echo unknown option $OPTION + ;; + esac + done + shift $((OPTIND - 1)) + + if [[ $text ]]; then + res="$(echo "$1" | sed "$min")" + else + res="$(sed "$min" "$1")" + fi + if [[ $out ]]; then + [[ $verbose ]] && echo "$res" | tee "$out" || echo "$res" > "$out" + else + echo "$res" + fi +} \ No newline at end of file diff --git a/modules/scripting/path.sh b/modules/scripting/path.sh new file mode 100644 index 0000000..8e99bbf --- /dev/null +++ b/modules/scripting/path.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +isAbsPath() { + if [[ "${1:0:1}" == / || "${1:0:2}" == ~[/a-z] ]] + then + echo "true" + return 0 + else + return 1 + fi +} + +# get full absolute path from a current or parent path +function abs_path { + local target="$1" + [[ $(isAbsPath $target) ]] && { echo $target; return 0; } + # echo relative path finding absolute + if [[ ! $(echo "$target" | sed 's/^\.\..*//') ]]; then + # echo .. parent directory + echo "$(dirname "$(pwd)")/${target/..//}" | tr -s / + return 0 + fi + if [[ ! $(echo "$target" | sed 's/^\..*//') ]]; then + # echo . current directory + echo "$(pwd)/${target/.//}" | tr -s / + return 0 + fi + # echo simple relative + echo "$(pwd)/$target" | tr -s / +} + +# get full absolute path from a current or parent path +function abs_dir { + # with no trailing / will assume that is a file + # and remove it so return only the directory. + local target="$1" + [[ ! "$target" == */ ]] && target=$(dirname $target) + echo $(abs_path $target) +} + + diff --git a/modules/utility/yaml.sh b/modules/scripting/yaml.mod similarity index 67% rename from modules/utility/yaml.sh rename to modules/scripting/yaml.mod index 0d8946d..e0c7f62 100755 --- a/modules/utility/yaml.sh +++ b/modules/scripting/yaml.mod @@ -2,6 +2,9 @@ # echo loading yaml module # https://gist.github.com/pkuczynski/8665367 # read yaml file, second argument (if any) is added prefix + +module_load minimize + parse_yaml() { local prefix=$2 local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034') @@ -19,7 +22,17 @@ parse_yaml() { } yaml() { - python3 -c "import yaml;print(yaml.safe_load(open('$1'))$2)" + dic=$(parse_yaml $1) + echo $dic + declare -A test + while read -d, -r pair; do + IFS='=' read -r key val <<<"$pair" + echo adding $val to $key + test[$key]=$val + done <<<"$dic\n" + echo ${test['MP']} + echo ${test[@]} } + # dic=$(python3 -c "import yaml;print(yaml.safe_load(open('$1')))") \ No newline at end of file diff --git a/modules/utility/iecho.func b/modules/utility/iecho.func new file mode 100644 index 0000000..9bc10ee --- /dev/null +++ b/modules/utility/iecho.func @@ -0,0 +1,16 @@ +#!/bin/bash +iecho () { +[[ ! -z "$PS1" ]] && echo $1 +} + +# no way to tell if sourced vs subshell +# # [[ ! -z "$PS1" ]] && [[ $SHLVL -eq 1 ]] && echo $1 +# # echo [[ -z $PS1 ]] +# echo $SHLVL +# echo $$ +# echo $BASHPID +# if [[ -t 1 ]]; then +# echo "Terminal" +# else +# echo "Not-a-terminal" +# fi \ No newline at end of file