diff --git a/ghostwheel.fn.sh b/ghostwheel.fn.sh index d4c74dd..9e07b5d 100755 --- a/ghostwheel.fn.sh +++ b/ghostwheel.fn.sh @@ -51,33 +51,82 @@ C4_T_OKAY="${C4_T_OKAY-OKAY}" C4_T_FAIL="${C4_T_FAIL-FAIL}" #### -#Lab: this ugly beast merges bash associate arrays +## Lab: this ugly beast merges bash associate arrays # (c=(`unset a b c; \ # declare -A a=([b]="bb" [c]="cc"); \ # declare -A b=([d]="dd"); declare -p a b | \ # perl -e '@a= map { chomp; s/^.*=\(|\)$//g; $_ } <>; print qq(@a)' \ # `); declare -p c) -# declare -A c=(["[c]=\"cc\" [b]=\"bb\" [d]=\"dd\" "]="" ) +## :Lab declare -A c=(["[c]=\"cc\" [b]=\"bb\" [d]=\"dd\" "]="" ) +#### +## Lab: this one can let us pass functions around +# (foo="$(declare -fx foo; \ +# function foo { \ +# echo hello;\ +# } ; \ +# export -pf foo; \ +# declare -pf foo; \ +# )" ; \ +# eval "$foo"; \ +# foo ) +## :Lab hello; #### -#### c4.maa - -### join valueset assignments of bash assoicative arrays +#### c4.f.dval +### output assignments from bash arrays ### (unset a b c; \ ### declare -A a=([b]="bb" [c]="cc"); \ ### declare -A b=([d]="dd");\ -### echo $( c4.maa a b ) ) -function c4.maa { - # avoid warning from declare -p when no arguments are given - test "$*" && \ - declare -p $@ \ - | perl -e ' # combine assignments: - $\=q( ); # 1. join ouput on space - print map { # 6. output the line - chomp; # 3. rm -f newline - s/^.*=\(|\)$//g; # 4. extract assignments - $_ # 5. use value (not replacment count) - } <>' # 2. walk input from declare -p -} #### -END c4.maa +### echo $( c4.f.dval a b ) ) +function c4.f.dval { + declare -p $@ | perl -e ' + # combine assignments: + $\=q( ); # 1. join ouput on space + print map { # 6. output the line + chomp; # 3. rm -f newline + s/^.*=\(|\)$//g; # 4. extract assignments + $_ # 5. use value (not replacment count) + } <>' # 2. walk input from declare -p +} +#### -END c4.f.dval + + +#### c4.f.prgs_Array - +### create associative array from (quoted) name list and args +function c4.f.pargs { + declare -la rv=() + while test -n "$1" ; do + rv+=$1; shift + done + c4.f.dval rv +} +#### -END c4.f.pargs_Array + +#### c4.f.sargs_Array - +### create array from (quoted) string list +function c4.f.sargs { + local -a rv; + local -i i=0; + for arg in $1 ; do + rv+=$arg; + done + c4.f.dval rv +} +#### -END c4.f.sargs + +#### c4.f.nrgs_Array - +### create associative array from (quoted) name list and args +#(eval "declare -lA v=($( . ghostwheel.fn.sh; c4.f.nargs "a b" "c" "d" ))"; echo "${v[a]}") #c +function c4.f.nargs { + local names="$1"; shift; + local -A rv; + local n; for n in $names ; do + rv[$n]="$1"; + shift; + done + c4.f.dval rv +} +#### -END c4.f.pargs_Array function c4.t.b { C4_TN=$((++C4_TN)) @@ -86,7 +135,7 @@ function c4.t.b { [tn]=$C4_TN [okrv]=0 [okay]="$C4_T_OKAY" [fail]="$C4_T_FAIL" [tags]="$1" [runl]="$2" [name]="$3" [desc]="$4" - ); shift; shift; shift; shift; + ); shift 4; # pick the first non-empty tag as a prefix for t in ${ta[tags]} ; do @@ -223,7 +272,7 @@ function c4.s.first { fi done } -c4.t.b "s" 0 "first" "[ x z] == z" test "x" == '$( c4.s.first "" "$foo" "x" "z" )' +c4.t.b "s" 0 "first" "[ x z] == x" test "x" == '$( c4.s.first "" "$foo" "x" "z" )' ## Return first non-empty item matching PATTERN from SET ## when NEGATE is ! or NOT return first non-empty, non- @@ -257,7 +306,7 @@ function c4.s.firstmatch { done } c4.t.b "s" 0 "firstmatch" "[x o x] == x" test "x" == '"$( c4.s.firstmatch "x" "o" "x" )"' -c4.t.b "s" 0 "firstmatch" "[!x o x] == o" test "o" == '"$( c4.s.firstmatch "!" "x" "" "x" "o" )"' +c4.t.b "s" 0 "firstmatch" "[!x x o] == o" test "o" == '"$( c4.s.firstmatch "!" "x" "" "x" "o" )"' ## give a friendly "did it work?" report ## TODO supress ANSI color-codes when STDOUT isn't a TTY @@ -325,10 +374,9 @@ function c4.updn { fi echo -e "Load \e[1;${C4_UPDN_NC}m${what}\e[0m [\e[1;${color}m${up}\e[0m]$msg" } -#for n in $( seq 0 64 ) ; do (C4_UPDN_GC="$n";c4.updn "foo" "UP" "$n"); done -#echo_updown "foo" "up" "bar" #[[OK][foo [UP] bar]] ### SYSUTIL +#( function foo { declare -p; }; foo) ## print hostname if current (or given) file is on a spoke function c4.s.host { @@ -413,163 +461,6 @@ c4.t.b "s" 0 "gw_is_localhost_f" "== hostname -f" test "0" == '"$(c4.gw_is_local c4.t.b "s" 0 "gw_is_localhost" "== hostname -s" test "0" == '"$(c4.gw_is_localhost `hostname -s`; echo "$?")"' c4.t.b "s" 0 "name" "!= moo" test "1" == '"$(c4.gw_is_localhost "moo"; echo "$?")"' -## check validity/availability of a node -_c4_is_gn= -function c4.i.is_gn { -seq "$(<.min)" "$(<.max)" | grep -v "$( perl -le 'printf q{\(%s\)}, join q{\|}, map {chomp;$_} <>' <.skip )" | grep "$(hostname -s | tr -d '[[:alpha:]]')" >/dev/null 2>&1 && echo OK -} - -## Create a shell script by reading STDIN -_c4_mksh_infh=/dev/stdin # read from STDIN by default -_c4_mksh_otfh=/dev/stdout # write to STDOUT by default -_c4_mksh_bash=/usr/bin/bash # script for bash by default -function c4.mksh { - local C4_MKSH_INFH="$(c4.s.first "$C4_MKSH_INFH" "$_c4_mksh_infh" /dev/stdin)" - local C4_MKSH_OTFH="$(c4.s.first "$1" "$C4_MKSH_OTFH" "$_c4_mksh_otfh" /dev/stdout)" - local C4_MKSH_BASH="$(c4.s.first "$2" "$C4_MKSH_BASH" "$_c4_mksh_bash" /usr/bin/bash)" - - printf '#!%s'"\n" "$C4_MKSH_BASH" >$C4_MKSH_OTFH - - while IFS= read -r line; do - echo "$line" >> $C4_MKSH_OTFH - done <$C4_MKSH_INFH - - # set the executable bit when making a regular file - test -f $C4_MKSH_OTFH && chmod a+x $C4_MKSH_OTFH -} - -# run command on rhost via ssh when not on rhost -_c4_ssh_debug="$(c4.s.first "$_C4_SSH_DEBUG" "$C4_DEBUG")" -_C4_SSH="${_C4_SSH:-ssh}" -function c4.ssh { - local host="$1"; shift; - if c4.gw_is_localhost "$host" ; then # run locally - if test -z "$_c4_ssh_debug" ; then - $@ - else - echo "$@" - fi - else # use ssh - local sshcmd="$( printf '%s %s' \ - "$(c4.s.first $C4_SSH $_C4_SSH "ssh")" \ - "$(c4.fqn "$host" )" \ - )"; - if test -z "$_c4_ssh_debug" ; then - $sshcmd $@ - else - echo "$sshcmd $@" - fi - fi - #local rhost="$( c4.gw_is_localhost "$1" && echo $1 )" -} - -# run command on rhost via ssh when not on rhost -_c4_ssh_debug="$(c4.s.first "$_C4_SSH_DEBUG" "$C4_DEBUG")" -_C4_SSH="${_C4_SSH:-ssh}" -function c4.ssh2 { - if test -z "$1" ; then - echo "WF:C4.ssh empty or mssing hostname" 1>&2 - return 1; - fi - - _c4_ssh_hi=$1; shift; - _c4_ssh_ho= - _c4_ssh_hs= - _c4_ssh_hc= - - # ensure we have both long and short name - if echo "$_c4_ssh_hi" | grep \. 1>/dev/null ; then - # TODO: could be smarter, given e.g. x.gw10.foo and is10 are gw10 - _c4_ssh_hs="$( echo "$_c4_ssh_hi" | cut -d '.' -f 1 )" - _c4_ssh_ho="$_c4_ssh_hi"; - else - _c4_hs="$_c4_ssh_hin"; - _c4_ho="$( c4.fqn $_c4_hs )"; - fi - - if test "$C4_GW" != "$_c4_ssh_hs" ; then - _c4_ssh_hc="$(printf '%s %s ' "$_C4_SSH" "$_c4_ssh_ho")"; - fi - - if test -z "$_c4_ssh_debug" ; then - $_c4_ssh_hc"$@" - else - #echo "$_c4_ssh_hc"'"'"$@"'"' - local pre= - local post= - if test -n "$_c4_ssh_hc" ; then - pre="'" - post="'" - fi - echo "$_c4_ssh_hc$pre$@$post" - fi -} - - -## run mkdir locally if the given spoke is up, otherwise use SSH -_c4_mkdir_debug="$(c4.s.first "$_C4_MKDIR_DEBUG" "$C4_DEBUG")" -function c4.mkdir { - _c4_mkdir_islocal="$( - c4.s.host "$( - c4.s.firstmatch ! - "$@" - )" - )" - if test -n "$_c4_mkdir_islocal" ; then - if test -z "$_c4_mkdir_debug" ; then - mkdir $@ - else - echo mkdir $@ - fi - else - c4.ssh mkdir $@ - fi -} - -## -_c4_rsync_debug="$(c4.s.first "$_C4_RSYNC_DEBUG" "$C4_DEBUG")" -function c4.rsync { - local m== "$( c4.s.firstmatch ":" "$@" )"; - if test -n "$m" ; then - m="$(echo "$m" | cut -d ':' -f 1)" - else - m="$(c4.s.host "$( c4.s.firstmatch ! ^ - "$@" )")" - fi - if test -n "$m" && c4.gw_is_localhost "$m"; then - if test -z "$_c4_rsync_debug" ; then - echo TROUBLE mkdir $@ - else - echo mkdir $@ - fi - else - c4.ssh mkdir $@ - fi -} - - -# run command on rhost via ssh when not on rhost -_c4_cmd_debug="$(c4.s.first "$_C4_CMD_DEBUG" "$C4_DEBUG")" -# function c4.cmd { - # _c4_mkdir_islocal="$( - # c4.s.host "$( - # c4.s.firstmatch ! - "$@" - # )" - # )" -# if test -n "$_c4_mkdir_islocal" ; then -# if test -z "$_c4_rsync_debug" ; then -# $_c4__cmdpre $@ -# else -# echo $_c4__cmdpre $@ -# fi -#} - -# run rsync on rhost via ssh when not on rhost -# function c4.rsync { -# if test -z "$_c4_rsync_debug" ; then -# $_c4_rsync_ssh $@ -# else -# echo $_c4_rsync_ssh $@ -# fi -# } ### IDENTITY ### - bootstrapping @@ -594,22 +485,57 @@ function c4.i.gn_max () { printf '%d' $C4_I_GN_MAX } - +C4_I_GN_SKIP= function c4.i.gn_skip { - local gn=$1; - - #if test -z "$gn" -o ! "$gn" -gt "0" + if test -n "$1" -o -z "$C4_I_GN_SKIP"; then + C4_I_GN_SKIP="$(c4.i.vals gn skip)"; + fi + echo $C4_I_GN_SKIP } -## return first identifier from source -## where SOURCE is a readable file -function c4.i.val { - +C4_I_GN_SKIPPED=() +function c4.i.gn_skipped { + local gn=$1; + if test -z "$C4_I_GN_SKIPPED"; then + C4_I_GN_SKIPPED[$gn]=1 + if test -n "$gn" \ + -a 0 -lt "$gn" \ + -a $(c4.i.gn_min) -le "$gn" \ + -a $(c4.i.gn_max) -ge "$gn" \ + >/dev/null 2>&1; then + C4_I_GN_SKIPPED[$gn]=0 + local n; + for n in $(c4.i.gn_skip) ; do + if test "$gn" == "$n" ; then + C4_I_GN_SKIPPED[$gn]=1; + break + fi + done + fi + fi + test 0 -eq "${C4_I_GN_SKIPPED[$gn]}" +} + +## check validity/availability of a node +_c4_is_gn= +#function c4.i.is_gn { +#seq "$(<.min)" "$(<.max)" | grep -v "$( perl -le 'printf q{\(%s\)}, join q{\|}, map {chomp;$_} <>' <.skip )" | grep "$(hostname -s | tr -d '[[:alpha:]]')" >/dev/null 2>&1 && echo OK +#} + + +function c4.fa_src { local src #="$(case $# in 1) $1 ;; *) $( c4.i.of $@ ) ;; esac) 1)"; shift; case $# in 1) src=$1 ;; *) src=$( c4.i.of $@ ) ;; esac + test -n "$src" && printf "%s" "$src" +} + +## return first identifier from source +## where SOURCE is a readable file +function c4.i.val { + local src=$( c4.fa_src $@ ) if test ! -r "$src" ; then # || test 0 == "$(file "$src" | grep text >2 2>&1; echo $?)" echo "WARN" "C4R${C4_RUN}${C4_TR}_VAL: missing SRC" >&2 # LOG @@ -618,21 +544,71 @@ function c4.i.val { head -1 $src } +## return source identifiers +## where SOURCE is a readable file +function c4.i.vals { + local src=$( c4.fa_src $@ ) + if test ! -r "$src" ; then + echo "WARN" "C4R${C4_RUN}${C4_TR}_VAL: missing SRC" >&2 # LOG + return 1; + fi + cat $src +} + ## call COMMAND for each identifier from SOURCE ## where SOURCE is a readable file function c4.i.for { - local src="$1"; shift; + local src=$( c4.fa_src $1 ) if test ! -r "$src" ; then - # || test 0 == "$(file "$src" | grep text >2 2>&1; echo $?)" echo "WARN" "C4R${C4_RUN}${C4_TR}_FOR: missing SRC" >&2 # LOG return 1; fi - local line=; - while read line ; do + + local line=; while read line ; do $@ "'""$line""'" done <$src } +function c4.i.autosave { + test -z "$C4_I_INHIBIT_AUTOSAVE" \ + || return; + + local src=$( c4.fa_src $1 ); shift + if test ! -r "$src" ; then + echo "WARN" "C4R${C4_RUN}${C4_TR}_AUTOSAVE: missing SRC" >&2 # LOG + return 1; + fi + + cp $src $src~ +} + +function c4.i.clear { + local src=$( c4.fa_src $1 ); shift + if test ! -r "$src" ; then + echo "WARN" "C4R${C4_RUN}${C4_TR}_CLEAR: missing SRC" >&2 # LOG + return 1; + fi + c4.i.autosave $src + printf ''>$src +} + +function c4.i.set { + local src=$( c4.fa_src $1 ); shift + if test ! -r "$src" ; then + echo "WARN" "C4R${C4_RUN}${C4_TR}_SET: missing SRC" >&2 # LOG + return 1; + fi + + c4.i.clear $src # clear handles autosave (when not inhibitied) + + local v; + for v in "$@" ; do + printf '%s'"\n" "$v" >>$src + done +} + + + ## return identity source of [[ [...]] query ## where QUERY (and CONTEXT1, etc, if provided) are words function c4.i.of { @@ -705,9 +681,117 @@ function c4.s.sshfs { echo "WARN" "C4R${C4_RUN}S_SSHFS: missing or non-directory TO \"$2\" (FROM \"$1\")" >&2 # LOG return 1; fi - : $C4_S_SSHFS_CMD $C4_S_SSHFS_OPT $@ + $C4_S_SSHFS_CMD $C4_S_SSHFS_OPT $@ } +## run mkdir locally if the given spoke is up, otherwise use SSH +_c4_mkdir_debug="$(c4.s.first "$_C4_MKDIR_DEBUG" "$C4_DEBUG")" +function c4.mkdir { + _c4_mkdir_islocal="$( + c4.s.host "$( + c4.s.firstmatch ! - "$@" + )" + )" + if test -n "$_c4_mkdir_islocal" ; then + if test -z "$_c4_mkdir_debug" ; then + mkdir $@ + else + echo mkdir $@ + fi + else + c4.ssh mkdir $@ + fi +} + +## Create a shell script by reading STDIN +_c4_mksh_infh=/dev/stdin # read from STDIN by default +_c4_mksh_otfh=/dev/stdout # write to STDOUT by default +_c4_mksh_bash=/usr/bin/bash # script for bash by default +function c4.mksh { + local C4_MKSH_INFH="$(c4.s.first "$C4_MKSH_INFH" "$_c4_mksh_infh" /dev/stdin)" + local C4_MKSH_OTFH="$(c4.s.first "$1" "$C4_MKSH_OTFH" "$_c4_mksh_otfh" /dev/stdout)" + local C4_MKSH_BASH="$(c4.s.first "$2" "$C4_MKSH_BASH" "$_c4_mksh_bash" /usr/bin/bash)" + + printf '#!%s'"\n" "$C4_MKSH_BASH" >$C4_MKSH_OTFH + + while IFS= read -r line; do + echo "$line" >> $C4_MKSH_OTFH + done <$C4_MKSH_INFH + + # set the executable bit when making a regular file + test -f $C4_MKSH_OTFH && chmod a+x $C4_MKSH_OTFH +} + +# run command on rhost via ssh when not on rhost +_c4_ssh_debug="$(c4.s.first "$_C4_SSH_DEBUG" "$C4_DEBUG")" +_C4_SSH="${_C4_SSH:-ssh}" +function c4.ssh { + local host="$1"; shift; + if c4.gw_is_localhost "$host" ; then # run locally + if test -z "$_c4_ssh_debug" ; then + $@ + else + echo "$@" + fi + else # use ssh + local sshcmd="$( printf '%s %s' \ + "$(c4.s.first $C4_SSH $_C4_SSH "ssh")" \ + "$(c4.fqn "$host" )" \ + )"; + if test -z "$_c4_ssh_debug" ; then + $sshcmd $@ + else + echo "$sshcmd $@" + fi + fi +} + +## +_c4_rsync_debug="$(c4.s.first "$_C4_RSYNC_DEBUG" "$C4_DEBUG")" +function c4.rsync { + local m== "$( c4.s.firstmatch ":" "$@" )"; + if test -n "$m" ; then + m="$(echo "$m" | cut -d ':' -f 1)" + else + m="$(c4.s.host "$( c4.s.firstmatch ! ^ - "$@" )")" + fi + if test -n "$m" && c4.gw_is_localhost "$m"; then + if test -z "$_c4_rsync_debug" ; then + echo TROUBLE mkdir $@ + else + echo mkdir $@ + fi + else + c4.ssh mkdir $@ + fi +} + + +# run command on rhost via ssh when not on rhost +_c4_cmd_debug="$(c4.s.first "$_C4_CMD_DEBUG" "$C4_DEBUG")" +# function c4.cmd { + # _c4_mkdir_islocal="$( + # c4.s.host "$( + # c4.s.firstmatch ! - "$@" + # )" + # )" +# if test -n "$_c4_mkdir_islocal" ; then +# if test -z "$_c4_rsync_debug" ; then +# $_c4__cmdpre $@ +# else +# echo $_c4__cmdpre $@ +# fi +#} + +# run rsync on rhost via ssh when not on rhost +# function c4.rsync { +# if test -z "$_c4_rsync_debug" ; then +# $_c4_rsync_ssh $@ +# else +# echo $_c4_rsync_ssh $@ +# fi +# } + #### #### END ####