From 9cab75803eb83b09b914f50b4f401671e203fa46 Mon Sep 17 00:00:00 2001 From: James Pannacciulli Date: Sat, 16 Sep 2017 18:12:33 -0700 Subject: refactor / reorganize / add some comments --- pars.sh | 213 +++++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 117 insertions(+), 96 deletions(-) diff --git a/pars.sh b/pars.sh index 670a552..4660517 100644 --- a/pars.sh +++ b/pars.sh @@ -15,12 +15,121 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +# help function +_parssh.usage () { + printf ' %s\n'\ + "parssh: Parallel SSH orchestration in a Bash session."\ + ""\ + "SYNOPSIS"\ + "parssh [-NUM] [-r|--rinput FILE] [-s|--servers FILE] [-b|--bare] [-c|--config]"\ + " [COMMANDS] [< SERVERS]"\ + ""\ + "DESCRIPTION"\ + " -NUM (default: 4)"\ + " Number of concurrent ssh connections to maintain. E.g. '-40'."\ + ""\ + " -r FILE, --rinput FILE (default: unset/inactive)"\ + " File to send as STDIN redirection on remote servers."\ + " (Can be used as replacement for or in conjunction with COMMANDS)."\ + ""\ + " -s FILE, --servers FILE (default: unset/inactive)"\ + " File to use as list of servers to run COMMANDS on."\ + " (Cannot be used in conjunction with a server list on STDIN)."\ + ""\ + " -b, --bare (default: unset/inactive)"\ + " Disable prepending of hostname to each output line returned by COMMANDS on SERVERS."\ + ""\ + " -C, --savecmd (default: unset/inactive)"\ + " Save the remote command line and local meta data as a header on the output."\ + ""\ + " -c SSH_OPTIONS, --config SSH_OPTIONS (default: unset/inactive)"\ + " Comma separated list of configuration parameters to be passed to SSH via '-o' flag."\ + ""\ + " -o PREFIX, --out PREFIX (default: unset/inactive)"\ + " Redirect STDOUT / STDERR to PREFIX.out / PREFIX.err, respectively."\ + ""\ + " -t PREFIX, --tee PREFIX (default: unset/inactive)"\ + " Copy STDOUT / STDERR to PREFIX.out / PREFIX.err, respectively."\ + ""\ + " COMMANDS"\ + " The list of commands to be executed remotely by SSH on each SERVER."\ + ""\ + " < SERVERS"\ + " Unless '-s' flag is used, STDIN will be used as list of remote servers."\ + " (Deliniated by newlines)."\ + ""\ + "NOTES"\ + " Given the nature of entering passwords manually, this function will likely not be"\ + " very useful without properly authorized SSH keys on all SERVERS." + return 1 +} + +# text processing functions +_parssh.host_prepend () + while read -r; do + printf "${_parssh_prepend_host+$host: }%s\n" "$REPLY" + done + +_parssh.out () { + local _parssh_out_stream="$1" + + while read -r; do + [[ "${_parssh_out_stdout}" == "true" ]] && \ + printf "%s\n" "$REPLY" + [[ "${_parssh_out_file}" == "true" ]] && \ + printf "%s\n" "$REPLY" >> "${_parssh_out_prefix}.${_parssh_out_stream}" + done +} + +_parssh.savecmd () { + CMDLINE=$(fc -ln -0) + CMDLINE="${CMDLINE#[[:space:]][[:space:]]}" + printf "### ###\n" + printf "### %s: %s\n"\ + date "$(date +%F@%R%z)"\ + user "$USER($UID)"\ + pwd "$PWD"\ + cmdline "$CMDLINE" + printf "### BEGIN parssh remote commands ###\n" + printf "%s\n### END parssh remote commands ###\n### ###\n"\ + "$@" +} + +# fd handlers +_parssh.serverlist_fd () { + [[ -z "$_parssh_servers" ]] && { + [[ -t 0 ]] && { + echo "No list of servers provided." + return 79 + } + exec 9<&0 + } || { + exec 9< <(printf "%s\n" "$_parssh_servers") + } +} + +_parssh.serverlist_fd_close () { + exec 9>&- +} + +# ssh workers +_parssh.ssh () { + ssh -n ${_parssh_ssh_config+-o} ${_parssh_ssh_config//,/ -o } $host -- "$@" +} + +_parssh.ssh_rinput () { + ssh -T ${_parssh_ssh_config+-o} ${_parssh_ssh_config//,/ -o } $host -- "$@"\ + < "$_parssh_rinput" +} + +# main parssh () { (( $# )) || { _parssh.usage return $? } + # preserve current bash options and ensure monitor (job control) is set local _parssh_origopts=$- set -m @@ -95,30 +204,12 @@ parssh () { shift done - [[ -z "$_parssh_servers" ]] && { - [[ -t 0 ]] && { - echo "No list of servers provided." - return 79 - } - exec 9<&0 - } || { - exec 9<"$_parssh_servers" - } + # load list of servers on fd 9 + _parssh.serverlist_fd + # run main command loop / capture outputs { - [[ "$_parssh_savecmd" == "true" ]] && { - CMDLINE=$(fc -ln -0) - CMDLINE="${CMDLINE#[[:space:]][[:space:]]}" - printf "### ###\n" - printf "### %s: %s\n"\ - date "$(date +%F@%R%z)"\ - user "$USER($UID)"\ - pwd "$PWD"\ - cmdline "$CMDLINE" - printf "### BEGIN parssh remote commands ###\n" - printf "%s\n### END parssh remote commands ###\n### ###\n"\ - "$@" - } + [[ "$_parssh_savecmd" == "true" ]] && _parssh.savecmd while read host; do while (( $(jobs -pr | wc -l) >= ${_parssh_concurrency:-4} )); do sleep 1 @@ -130,79 +221,9 @@ parssh () { wait - exec 9>&- - [[ "${_parssh_origopts//[^m]/}" == "m" ]] || set +m -} + # close serverlist fd + _parssh.serverlist_fd close -_parssh.ssh () { - ssh -n ${_parssh_ssh_config+-o} ${_parssh_ssh_config//,/ -o } $host -- "$@" -} - -_parssh.ssh_rinput () { - ssh -T ${_parssh_ssh_config+-o} ${_parssh_ssh_config//,/ -o } $host -- "$@"\ - < "$_parssh_rinput" -} - -_parssh.host_prepend () - while read -r; do - printf "${_parssh_prepend_host+$host: }%s\n" "$REPLY" - done - -_parssh.out () { - local _parssh_out_stream="$1" - - while read -r; do - [[ "${_parssh_out_stdout}" == "true" ]] && \ - printf "%s\n" "$REPLY" - [[ "${_parssh_out_file}" == "true" ]] && \ - printf "%s\n" "$REPLY" >> "${_parssh_out_prefix}.${_parssh_out_stream}" - done -} - -_parssh.usage () { - printf ' %s\n'\ - "parssh: Parallel SSH orchestration in a Bash session."\ - ""\ - "SYNOPSIS"\ - "parssh [-NUM] [-r|--rinput FILE] [-s|--servers FILE] [-b|--bare] [-c|--config]"\ - " [COMMANDS] [< SERVERS]"\ - ""\ - "DESCRIPTION"\ - " -NUM (default: 4)"\ - " Number of concurrent ssh connections to maintain. E.g. '-40'."\ - ""\ - " -r FILE, --rinput FILE (default: unset/inactive)"\ - " File to send as STDIN redirection on remote servers."\ - " (Can be used as replacement for or in conjunction with COMMANDS)."\ - ""\ - " -s FILE, --servers FILE (default: unset/inactive)"\ - " File to use as list of servers to run COMMANDS on."\ - " (Cannot be used in conjunction with a server list on STDIN)."\ - ""\ - " -b, --bare (default: unset/inactive)"\ - " Disable prepending of hostname to each output line returned by COMMANDS on SERVERS."\ - ""\ - " -C, --savecmd (default: unset/inactive)"\ - " Save the remote command line and local meta data as a header on the output."\ - ""\ - " -c SSH_OPTIONS, --config SSH_OPTIONS (default: unset/inactive)"\ - " Comma separated list of configuration parameters to be passed to SSH via '-o' flag."\ - ""\ - " -o PREFIX, --out PREFIX (default: unset/inactive)"\ - " Redirect STDOUT / STDERR to PREFIX.out / PREFIX.err, respectively."\ - ""\ - " -t PREFIX, --tee PREFIX (default: unset/inactive)"\ - " Copy STDOUT / STDERR to PREFIX.out / PREFIX.err, respectively."\ - ""\ - " COMMANDS"\ - " The list of commands to be executed remotely by SSH on each SERVER."\ - ""\ - " < SERVERS"\ - " Unless '-s' flag is used, STDIN will be used as list of remote servers."\ - " (Deliniated by newlines)."\ - ""\ - "NOTES"\ - " Given the nature of entering passwords manually, this function will likely not be"\ - " very useful without properly authorized SSH keys on all SERVERS." - return 1 + # restore bash options if modified + [[ "${_parssh_origopts//[^m]/}" == "m" ]] || set +m } -- cgit v1.2.3