# shellcheck disable=SC2148 disable=SC2034 declare Version=2.4.0 #============================================================================== # Copyright and license info is available in the LICENSE file included with # the Server Deployment Package (SDP), and also available online: # https://swarm.workshop.perforce.com/projects/perforce-software-sdp/view/main/LICENSE #------------------------------------------------------------------------------ #============================================================================== # Library Functions. #------------------------------------------------------------------------------ # Function: run # # Short: Run a command with optional description, honoring global $VERBOSITY # and $NO_OP settings. Simpler than runCmd(). # # Input: # $1 - cmd. The command to run. The command is displayed first # if $VERBOSITY > 3. # $2 - desc. Text description of command to run. # Optionally, the text string can be prefixed with 'N:', where N is # a one-digit integer, the minimum verbosity at which the description is to # be displayed. # Optionally, a series of text strings can be provided, delimited by '|', # allowing multiple descriptions to be provided, each with a different # minimum verbosity setting. # This parameter is optional. If the N: prefix is omitted, it is # equivalent to "3:". Sample values: "5:Cool command here:", # and "3:Regular verbosity message|4:Higher verbosity message." # and "This is a very|long description." # $3 - honorNoOpFlag. Pass in 1 to mean "Yes, honor the $NO_OP setting # and display (but don't run) commands if $NO_OP is set." Otherwise # $NO_OP is ignored, and the command is always run. # This parameter is optional; the default value is 1. # $4 - alwaysShowOutputFlag. If set to 1, show output regardless of $VERBOSITY # value. Otherwise, only show output if VERBOSITY > 4. # This parameter is optional; the default value is 0. # $5 - grepString. If set, this changes the exit code behavior. # If the specified string exists in the output, a 0 is returned, else 1. # Strings suitable for use with 'egrep' are allowed. # # Description: # Display an optional description of a command, and then run the # command. This is affected by $NO_OP and $VERBOSITY. If $NO_OP is # set, the command is shown, but not run, provided $honorNoOpFlag is 1. # The description is not shown if $VERBOSITY < 3. # # The variables CMDLAST and CMDEXITCODE are set each time runCmd is called. # CMDLAST contains the last command run. CMDEXITCODE contains its exit code. # # Output is shown if either $AlwaysShowOutputFlag is 1 or $VERBOSITY >= 4. #------------------------------------------------------------------------------ function run () { dbg "CALL: run ($*)" local cmd=${1:-} local desc=${2:-} local -i honorNoOpFlag=${3:-1} local -i alwaysShowOutputFlag=${4:-0} local grepString=${5:-} local cmdScript= local cmdOut= local -i grepExit CMDLAST=$cmd CMDEXITCODE=0 if [[ -n "$desc" ]]; then # Descriptions intended to be multi-line contain a '|' char. # Each entry may contain an optional 'N:' prefix where N is the # verbosity level at or above which the message is displayed. echo "$desc" | tr '|' '\n' | while read -r text; do if [[ $text =~ ^[0-9]+: ]]; then descVerbosity=${text%%:*} text=${text#$descVerbosity:} if [[ $VERBOSITY -ge $descVerbosity ]]; then echo -e "$text" fi else msg "$desc" fi done fi if [[ $honorNoOpFlag -eq 1 && $NO_OP -eq 1 ]]; then msg "NO-OP: Would run: \"$cmd\"\n" else msg "Running: \"$cmd\"." cmdScript="$(mktemp -t run.XXXXXX).cmd.sh" cmdOut="${cmdScript%.cmd.sh}.out" echo -e "#!/bin/bash\n$cmd\n" > "$cmdScript" chmod +x "$cmdScript" $cmdScript > "$cmdOut" 2>&1 CMDEXITCODE=$? if [[ -n "$grepString" ]]; then # shellcheck disable=SC2196 egrep "$grepString" "$cmdOut" > /dev/null 2>&1 grepExit=$? CMDEXITCODE="$grepExit" fi if [[ $alwaysShowOutputFlag -eq 1 ]]; then cat "$cmdOut" else [[ $VERBOSITY -gt 3 ]] && cat "$cmdOut" fi # Be clean and tidy. /bin/rm -f "$cmdScript" "$cmdOut" # If a grep was requested, return the exit code from the egrep, # otherwise return the exit code of the command executed. In # any case, $CMDEXITCODE contains the exit code of the command. if [[ -n "$grepString" ]]; then return $grepExit else return $CMDEXITCODE fi fi return 0 } #------------------------------------------------------------------------------ # Function: rrun # # Short: Run a command with on a remote host optional description, honoring # $VERBOSITY and $NO_OP settings. Simpler than runRemoteCmd(). # # Input: # $1 - host. The remote host to run the command on. # $2 - cmd. The command to run. The command is displayed first # if $VERBOSITY > 3. # $3 - desc. Text description of command to run. # Optionally, the text string can be prefixed with 'N:', where N is # a one-digit integer, the minimum verbosity at which the description is to # be displayed. # Optionally, a series of text strings can be provided, delimited by '|', # allowing multiple descriptions to be provided, each with a different # minimum verbosity setting. # This parameter is optional. If the N: prefix is omitted, it is # equivalent to "3:". Sample values: "5:Cool command here:" # $4 - honorNoOpFlag. Pass in 1 to mean "Yes, honor the $NO_OP setting # and display (but don't run) commands if $NO_OP is set." Otherwise # $NO_OP is ignored, and the command is always run. # This parameter is optional; the default value is 1. # $5 - alwaysShowOutputFlag. If set to 1, show output regardless of $VERBOSITY # value. Otherwise, only show output if VERBOSITY > 4. # This parameter is optional; the default value is 0. # $6 - grepString. If set, this changes the exit code behavior. # If the specified string exists in the output, a 0 is returned, else 1. # Strings suitable for use with 'egrep' are allowed. # # Description: # Display an optional description of a command, and then run the # command. This is affected by $NO_OP and $VERBOSITY. If $NO_OP is # set, the command is shown, but not run, provided $honorNoOpFlag is 1. # The description is not shown if $VERBOSITY < 3. # # The variables RCMDLAST and RCMDEXITCODE are set each time runCmd is called. # RCMDLAST contains the last command run. RCMDEXITCODE contains its exit code. # # Output is shown if either $AlwaysShowOutputFlag is 1 or $VERBOSITY >= 4. #------------------------------------------------------------------------------ function rrun () { dbg "CALL: rrun ($*)" local host=${1:-Unset} local cmd=${2:-Unset} local desc=${3:-} local -i honorNoOpFlag=${4:-1} local -i alwaysShowOutputFlag=${5:-0} local grepString=${6:-} local rCmdScript= local rCmdOut= local -i grepExit RCMDLAST="$cmd" RCMDEXITCODE=0 if [[ -n "$desc" ]]; then echo "$desc" | tr '|' '\n' | while read -r text; do if [[ $text =~ ^[0-9]+: ]]; then descVerbosity=${text%%:*} text=${text#$descVerbosity:} if [[ $VERBOSITY -ge $descVerbosity ]]; then echo -e "$text" fi else msg "$desc" fi done fi if [[ $honorNoOpFlag -eq 1 && $NO_OP -eq 1 ]]; then msg "NO-OP: Would run: \"$cmd\" on host $host.\n" else msg "Running: \"$cmd\" on host $host." rCmdScript="$(mktemp -t rrun.XXXXXX).cmd.sh" rCmdOut="${rCmdScript%.cmd.sh}.out" echo -e "#!/bin/bash\n$cmd\n" > "$rCmdScript" chmod +wx "$rCmdScript" if ! scp -pq "$rCmdScript" "$host:${P4TMP:-/tmp}/."; then RCMDEXITCODE=-1 errmsg "rrun(): Failed to copy temp command script to $host." return 1 fi ssh -q -n "$host" "$rCmdScript" > "$rCmdOut" 2>&1 RCMDEXITCODE=$? if [[ -n "$grepString" ]]; then # shellcheck disable=SC2196 egrep "$grepString" "$rCmdOut" > /dev/null 2>&1 grepExit=$? RCMDEXITCODE="$grepExit" fi if [[ $alwaysShowOutputFlag -eq 1 ]]; then cat "$rCmdOut" else [[ $VERBOSITY -gt 3 ]] && cat "$rCmdOut" fi # Be clean and tidy. /bin/rm -f "$rCmdScript" "$rCmdOut" # If a grep was requested, return the exit code from the egrep, # otherwise return the exit code of the command remotely executed. # In any case, $RCMDEXITCODE contains the exit code of the command. if [[ -n "$grepString" ]]; then return $grepExit else return $RCMDEXITCODE fi fi return 0 }