How to wait in bash for several subprocesses to finish and return exit code !=0 when any subprocess ends with code !=0?

后端 未结 30 2473
悲哀的现实
悲哀的现实 2020-11-22 03:50

How to wait in a bash script for several subprocesses spawned from that script to finish and return exit code !=0 when any of the subprocesses ends with code !=0 ?

S

30条回答
  •  别那么骄傲
    2020-11-22 04:33

    Here's my version that works for multiple pids, logs warnings if execution takes too long, and stops the subprocesses if execution takes longer than a given value.

    function WaitForTaskCompletion {
        local pids="${1}" # pids to wait for, separated by semi-colon
        local soft_max_time="${2}" # If execution takes longer than $soft_max_time seconds, will log a warning, unless $soft_max_time equals 0.
        local hard_max_time="${3}" # If execution takes longer than $hard_max_time seconds, will stop execution, unless $hard_max_time equals 0.
        local caller_name="${4}" # Who called this function
        local exit_on_error="${5:-false}" # Should the function exit program on subprocess errors       
    
        Logger "${FUNCNAME[0]} called by [$caller_name]."
    
        local soft_alert=0 # Does a soft alert need to be triggered, if yes, send an alert once 
        local log_ttime=0 # local time instance for comparaison
    
        local seconds_begin=$SECONDS # Seconds since the beginning of the script
        local exec_time=0 # Seconds since the beginning of this function
    
        local retval=0 # return value of monitored pid process
        local errorcount=0 # Number of pids that finished with errors
    
        local pidCount # number of given pids
    
        IFS=';' read -a pidsArray <<< "$pids"
        pidCount=${#pidsArray[@]}
    
        while [ ${#pidsArray[@]} -gt 0 ]; do
            newPidsArray=()
            for pid in "${pidsArray[@]}"; do
                if kill -0 $pid > /dev/null 2>&1; then
                    newPidsArray+=($pid)
                else
                    wait $pid
                    result=$?
                    if [ $result -ne 0 ]; then
                        errorcount=$((errorcount+1))
                        Logger "${FUNCNAME[0]} called by [$caller_name] finished monitoring [$pid] with exitcode [$result]."
                    fi
                fi
            done
    
            ## Log a standby message every hour
            exec_time=$(($SECONDS - $seconds_begin))
            if [ $((($exec_time + 1) % 3600)) -eq 0 ]; then
                if [ $log_ttime -ne $exec_time ]; then
                    log_ttime=$exec_time
                    Logger "Current tasks still running with pids [${pidsArray[@]}]."
                fi
            fi
    
            if [ $exec_time -gt $soft_max_time ]; then
                if [ $soft_alert -eq 0 ] && [ $soft_max_time -ne 0 ]; then
                    Logger "Max soft execution time exceeded for task [$caller_name] with pids [${pidsArray[@]}]."
                    soft_alert=1
                    SendAlert
    
                fi
                if [ $exec_time -gt $hard_max_time ] && [ $hard_max_time -ne 0 ]; then
                    Logger "Max hard execution time exceeded for task [$caller_name] with pids [${pidsArray[@]}]. Stopping task execution."
                    kill -SIGTERM $pid
                    if [ $? == 0 ]; then
                        Logger "Task stopped successfully"
                    else
                        errrorcount=$((errorcount+1))
                    fi
                fi
            fi
    
            pidsArray=("${newPidsArray[@]}")
            sleep 1
        done
    
        Logger "${FUNCNAME[0]} ended for [$caller_name] using [$pidCount] subprocesses with [$errorcount] errors."
        if [ $exit_on_error == true ] && [ $errorcount -gt 0 ]; then
            Logger "Stopping execution."
            exit 1337
        else
            return $errorcount
        fi
    }
    
    # Just a plain stupid logging function to be replaced by yours
    function Logger {
        local value="${1}"
    
        echo $value
    }
    

    Example, wait for all three processes to finish, log a warning if execution takes loger than 5 seconds, stop all processes if execution takes longer than 120 seconds. Don't exit program on failures.

    function something {
    
        sleep 10 &
        pids="$!"
        sleep 12 &
        pids="$pids;$!"
        sleep 9 &
        pids="$pids;$!"
    
        WaitForTaskCompletion $pids 5 120 ${FUNCNAME[0]} false
    }
    # Launch the function
    someting
        
    

提交回复
热议问题