From b7b2d7061a62f0012c680717d546c9293386673c Mon Sep 17 00:00:00 2001 From: David Kebler Date: Wed, 21 Feb 2024 11:11:07 -0800 Subject: [PATCH] improve module base - use stderr for error messages helpers: added mkrfilename for making random file names instead of using mktemp remote_arg function escapes spaces within an arugment(s) for use passing remotely --- README.md | 4 ++ module.base | 25 ++++---- module.base.src | 36 ++++++------ modules/scripting/bundle.func | 104 +++++++++++++++++++--------------- modules/scripting/debug.lib | 1 + modules/scripting/helpers.lib | 53 +++++++++++++++-- modules/scripting/iecho.func | 2 +- 7 files changed, 145 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index 0386068..86782fa 100644 --- a/README.md +++ b/README.md @@ -64,3 +64,7 @@ interactive non-login shells source /etc/profile via /etc/bash.bashrc otherwise Thus for non-interactive/non-login shells it is best to symlink a non-module file/directory in modules folder in order to give access to non-interactive shells + +TODO + +* conditionally create a `which` function` in shell.env if which command is not availble. which being pretty much the same as `command -v cmd` \ No newline at end of file diff --git a/module.base b/module.base index b062c9f..01f8b9c 100644 --- a/module.base +++ b/module.base @@ -10,15 +10,14 @@ if [ "$FILE" ]; then COUNT=$(echo $FILE | xargs -n 1 | wc -l) fi if [ $COUNT -gt 1 ]; then - echo two or more modules of same name found, aborting - echo $FILE | xargs -n 1 + >&2 echo two or more modules of same name found, aborting + >&2 echo $FILE | xargs -n 1 return 1 fi -[ $COUNT == 1 ] && echo $FILE && return 0 -return 1 +[[ $COUNT == 1 ]] && echo $FILE } module_find() { -[ ! $1 ] && echo "no module specified" && return 1 +[ ! $1 ] && >&2 echo "no module specified" && return 1 local MDIRS local MDIR local DIRS @@ -36,17 +35,21 @@ for ((i=1;i<=cnt;i++)); do [ $? -eq 0 ] && echo $RES && return 0 fi done + >&2 echo no module found anywhere return 1 } module_load() { -[ ! $1 ] && echo "no module specified" && return 1 +local module;local mpath +[ ! $1 ] && >&2 echo "no module specified" && return 1 local FILE -FILE=$(module_find $1) -[ $? -ne 0 ] && echo no module $1 found && return 1 -shift 1 -source $FILE "$@" -return 0 +for module in "$@" +do + eval "_${module}_ () { echo "$fname test"; }" + mpath=$(module_find $module) + [ $? -ne 0 ] && >&2 echo extreme warning: no module $1 found, so was not loaded + source $mpath "$@" +done } shell_run () { bash -ci ". ${1}" diff --git a/module.base.src b/module.base.src index bba73e0..03e93c9 100644 --- a/module.base.src +++ b/module.base.src @@ -19,20 +19,18 @@ COUNT=0 if [ "$FILE" ]; then COUNT=$(echo $FILE | xargs -n 1 | wc -l) fi -# echo Number: $COUNT if [ $COUNT -gt 1 ]; then - echo two or more modules of same name found, aborting - echo $FILE | xargs -n 1 + >&2 echo two or more modules of same name found, aborting + >&2 echo $FILE | xargs -n 1 return 1 fi -[ $COUNT == 1 ] && echo $FILE && return 0 -return 1 +[[ $COUNT == 1 ]] && echo $FILE } # Returns path to module if succesful module_find() { -[ ! $1 ] && echo "no module specified" && return 1 +[ ! $1 ] && >&2 echo "no module specified" && return 1 local MDIRS local MDIR @@ -65,22 +63,28 @@ for ((i=1;i<=cnt;i++)); do [ $? -eq 0 ] && echo $RES && return 0 fi done - # no module found anywhere + >&2 echo no module found anywhere return 1 } module_load() { -[ ! $1 ] && echo "no module specified" && return 1 -# (return 0 2>/dev/null) && echo "module_load was sourced" || echo "module_log was executed" +local module;local mpath +[ ! $1 ] && >&2 echo "no module specified" && return 1 local FILE -FILE=$(module_find $1) -[ $? -ne 0 ] && echo no module $1 found && return 1 -# source $FILE "$0" -# [[ $BASHPID -eq $$ ]] || echo $FILE -shift 1 -source $FILE "$@" -return 0 +for module in "$@" +do +# is there way to not reload modules?? +# if [[ $(eval "echo \"\$MODULE_$module_LOADED\"") ]]; then +# echo $module already loaded +# else + eval "_${module}_ () { echo "$fname test"; }" + mpath=$(module_find $module) + [ $? -ne 0 ] && >&2 echo extreme warning: no module $1 found, so was not loaded + source $mpath "$@" +# eval "MODULE_$module_LOADED=true" +# fi +done } # put here so it's globally available diff --git a/modules/scripting/bundle.func b/modules/scripting/bundle.func index 763d884..f3e3fd3 100644 --- a/modules/scripting/bundle.func +++ b/modules/scripting/bundle.func @@ -9,64 +9,69 @@ # source <(echo -e "$scpt") module_load debug module_load file +module_load helpers + +# TODO keep debug statements if requested, other remove them bundle () { - local file - local code - local compact - if [[ $1 == "-c" ]]; then - module_load helpers - compact=true - shift 1 - fi - - # todo add -c for compacting the bundle, change this to options parsing. - - if [[ "$1" ]]; then - if [[ -f $1 ]]; then - file=$1 - else - if [[ $1 == "-m" ]]; then - file=$(module_find $2) - shift 1 - else - if [[ $1 == "-mf" ]]; then - module_load $2 - code="$(declare -f $3)" - shift 1 - else - code="$1" - shift 1 - fi - fi - fi - else - echo "aborting, no file, module or function passed for bundling" - return 1 - fi + local file; local xfunc; local compact; local func; + + debug $( ( IFS=$','; echo bundle arguments: "$*" ) ) + + declare OPTION; declare OPTARG; declare OPTIND + while getopts 'cf:x:' OPTION; do + case "$OPTION" in + c) + compact=true + ;; + f) + # can pass any arguments along with the function to run. Use " " for any argument with spaces + func="$OPTARG" + ;; + x) + # will extract a function from a file or module to be used for bundling + xfunc="$OPTARG" + ;; + *) + echo unknown bundle option $OPTION + ;; + esac + done + + shift $(( OPTIND - 1 )) tmp_file=$( mktemp -t TEMP_FILE_bundle.XXXXXXXX ) chmod 600 "$tmp_file" - # if [[ $2 == "__recurse__" ]] || [[ $file ]]; then - if [[ $file ]]; then - \cp $file $tmp_file - else - echo "$code" > $tmp_file - fi - - + + if [[ -f $1 ]]; then file=$1; else + if ! file=$(module_find "$1"); then >&2 echo fatal: bundle error, no module of name: $1; file=""; fi; fi + [[ ! $file ]] && >&2 echo "fatal: bundle error aborting, no file, module passed for bundling" && return 1 + \cp $file $tmp_file + if [[ $xfunc ]]; then + ( source $file; declare -f $xfunc > $tmp_file ) + fi + if [[ ! $2 == "__recurse__" ]]; then _modules="" output=$2 - debug outputing bundle to: $output + [[ $output ]] && debug outputing bundle to: $output || debug outputting bundle to console debug "bash code to bundle \n $(cat $tmp_file)" fi modules=$(sed -n -e 's/^\s*module_load //p' < $tmp_file) + # if [[ ( ! $modules ) && ( ! $2 == "__recurse__" ) ]]; then + # if [[ $output ]]; then + # \cp $tmp_file $output + # else + # echo -e "$(cat $tmp_file)" + # fi + # fi + # remove debug module by default so it doesn't get bundled + if [[ $modules ]]; then - # remove found module_load before continuing + # remove all found module_load before continuing sed -i '/^\s*module_load/d' $tmp_file [[ $_modules ]] && debug "already bundled modules: $_modules" for module in $(printf '%s\n' "${modules[@]}"|tac); @@ -86,14 +91,19 @@ bundle () { # the recursion is done if [[ ! $2 == "__recurse__" ]]; then - [[ $compact ]] && compact_file $tmp_file - if [[ $BASH_DEBUG ]]; then + [[ $compact ]] && compact_file $tmp_file + # TODO remove debug statements by default + # remove any default function calls + sed -i '/\s*(\s*return 0 2>\/dev\/null\s*)\s*|| /d' $tmp_file + # add in a function call if requested + [[ $func ]] && echo -e "\n $func \"\$@\"" >> $tmp_file + if [[ $BASH_DEBUG ]]; then debug "\n ------------code as bundled--------------\n \ $(cat $tmp_file) \ \n ------------------------------------------------------------ \n" debug "bundled modules were $_modules" fi - if [[ $2 ]]; then + if [[ $output ]]; then \cp $tmp_file $output else echo -e "$(cat $tmp_file)" @@ -105,3 +115,5 @@ bundle () { # # if script was executed then call the function (return 0 2>/dev/null) || bundle $@ + + diff --git a/modules/scripting/debug.lib b/modules/scripting/debug.lib index c4df041..cac6e53 100644 --- a/modules/scripting/debug.lib +++ b/modules/scripting/debug.lib @@ -1,5 +1,6 @@ #!/bin/bash # TODO allow debug to have managment flags and levels +# TODO filter debug statements to a particular module function debug () { if [[ $BASH_DEBUG ]]; then diff --git a/modules/scripting/helpers.lib b/modules/scripting/helpers.lib index 3e9bba8..ec11450 100644 --- a/modules/scripting/helpers.lib +++ b/modules/scripting/helpers.lib @@ -61,25 +61,50 @@ parse_option () { # otherwise just the options value local opts;local f;local opt; local ret [[ $1 = "-f" ]] && { f=true;shift 1; } - [[ $1 && $2 ]] || return 1 + [[ ! $# -eq 2 ]] && return 1 opts=$1 opt=$2 - [[ ! $opts =~ "$opt" ]] && return 1 - ret=$(sed -n "/^.*$opt\s\+\(\w\+\).*$/s//\1/p" <<< $opts) +# echo $opts, $opt + [[ ! $opts =~ $opt ]] && return 1 + ret=$(sed -n '/^.*'"$opt"'\s\+\(\w\+\).*$/s//\1/p' <<< $opts) [[ $f ]] && echo "$2 $ret" || echo $ret } +# TODO: need to pass two arrays and then pass them back, see -r ret in ssh for example +# parse_extra_args () { +# if [[ ! $* =~ "--" ]]; then +# debug $( ( IFS=$','; echo all arguments: "$*" ) ) +# for ((d=1; d<$#; ++d)); do +# [[ ${!d} == "--" ]] && break +# done +# if [[ $d -lt $# ]]; then # if there are extra ssh arguments +# debug found -- at $d +# opts=("${@:$d+1:$#}") +# debug $( ( IFS=$','; echo "ssh arguments: ${opts[*]}" ) ) +# # [[ ! ${sshargs[0]} ]] && { echo missing remote machine, must provide at least a hostname, -- hostname; return 3; } +# ropts=("${@:1:$d-1}") +# debug $( ( IFS=$','; echo "remaining arguments to parse: ${ropts[*]}" ) ) +# fi +# fi +# } + remove_empty_lines () { if [[ -f $1 ]]; then cat $1; else echo "$1"; fi | sed -n '/^\s*$/!p' # sed -rz 's/^\n+//; s/\n+$/\n/g' } +remote_args () { + local rargs + for i; do rargs="$rargs $(sed 's/ /\\ /g' <<< $i)"; done + echo $rargs +} + remove_end_spaces () { del=${2:-\'} # echo delimiter: $del - # sed -e "s/[[:space:]]\{1,\}$del/$del/" <<< "$1" - res=$(sed -e "s/^$del[[:space:]]*/$del/" <<< "$1") - sed -e "s/[[:space:]]*${del}$/$del/" <<< "$res" + # sed -e 's/[[:space:]]\{1,\}$del/$del/' <<< "$1" + res=$(sed -e 's/^$del[[:space:]]*/$del/' <<< "$1") + sed -e 's/[[:space:]]*${del}$/$del/' <<< "$res" } # pass any sed ' ' string and comments lines will be ignored @@ -102,8 +127,24 @@ compact_file () { # if [[ -f "$1" ]]; then cat "$1"; else echo -e "$@"; fi | sed 's/\\s*$//g' | sed -r '/^\s*$/d' | grep -v '^\s*#' } +mkrfilename () { + echo ${2:-/tmp/}${1:-file}.$RANDOM +} + +sshtest () { + echo running on machine: $(hostnamectl hostname) + echo as user:home $(whoami):$HOME + echo argument1 $1 + echo argument2 $2 + echo remaining arguments: "${@:3}" + echo home directory listing + ls -la ~ +} + #rows between matched rows # sed '1,/firstmatch/d;/secondmatch/,$d' +# this will escape ' for all lines having sed +# sed -i '/sed/s/\x27/\\'"'"'/g' $tmp_file \ No newline at end of file diff --git a/modules/scripting/iecho.func b/modules/scripting/iecho.func index 70df16b..f3dfce3 100644 --- a/modules/scripting/iecho.func +++ b/modules/scripting/iecho.func @@ -1,6 +1,6 @@ #!/bin/bash iecho () { -[[ ! -z "$PS1" ]] && echo $1 +[[ ! -z "$PS1" ]] && echo "$@" return 0 }