1

How to wait in bash script to subprocess and if one of them return exit code 1 so I want to stop all subprocess.

This is what I tried to do. But there are a some of issues:

  1. If the first process is longer than all the others, and another process fails in the background ... then the script waits for the first process to finish, even though another process has already failed.

  2. Can't detect that doSomething failed because I use pipe for the desired print format.

    #!/bin/bash
    
    function doSomething()
    {
            echo [ $1 start ]
    
            sleep $1
    
            if [ $1 == 10 ]; then
                    failed
            fi
    
            echo [ sleep $1 ]: done
    }
    
    function failed(){
                    sleep 2
                    echo ------ process failed ------
                    exit 1
    }
    
    function process_log() {
            local NAME=$1
            while read Line; do
                    echo [Name ${NAME}]: ${Line}
            done
    }
    
    pids=""
    
    
    (doSomething 4 | process_log 4)&
    pids+="$! "
    
    (doSomething 17 | process_log 17)&
    pids+="$! "
    
    (doSomething 6 | process_log 6)&
    pids+="$! "
    
    (doSomething 10 | process_log 10)&
    pids+="$! "
    
    (doSomething 22 | process_log 22)&
    pids+="$! "
    
    (doSomething 5 | process_log 5)&
    pids+="$! "
    
    
    for pid in $pids; do
           wait $pid || (pkill -P $$ ; break)
    done
    
    echo done program

Anyone have an idea?

John
  • 13
  • 4
  • No, it's not solve my problem.. Thanks for your help! @Fravadona – John Apr 14 '22 at 14:31
  • In fact https://stackoverflow.com/a/71778627/3387716 basically does what you're looking for. Just replace the `exit 1` (that you shouldn't have in the `failed` function) with a call to the `diex` function. – Fravadona Apr 14 '22 at 16:56
  • it's kill all the subprocess but also the parent process.. I want that the code in parent process continue to run.. in this example need will be print "done program" – John Apr 18 '22 at 13:10
  • Alright, your current use-case is different, so it's not a duplicate question. Check my answer to see if it does what you want now. – Fravadona Apr 19 '22 at 09:47
  • Alright, your current use-case is not a duplicate question. Check my updated answer. – Fravadona Apr 20 '22 at 10:49

1 Answers1

0

The gist of it would be:

#!/bin/bash
set -m # needed for using negative PIDs
trap '{ kill -- $(jobs -rp | sed s/^/-/); wait; } 2> /dev/null' USR1

doSomething() {
    echo "[ $1 start ]"
    sleep "$1"
    [[ $1 == 10 ]] && failed
    echo "[ sleep $1 ]: done"
}

failed(){
    echo "------ process failed ------" 1>&2
    kill -USR1 "$$"
}

process_log() {
    local name="$1" line
    while IFS='' read -r line; do
        echo "[Name $name]: $line"
    done
}

{ doSomething  4 | process_log  4; } &
{ doSomething 17 | process_log 17; } &
{ doSomething  6 | process_log  6; } &
{ doSomething 10 | process_log 10; } &
{ doSomething 22 | process_log 22; } &
{ doSomething  5 | process_log  5; } &

wait

echo "done program"
[Name 4]: [ 4 start ]
[Name 6]: [ 6 start ]
[Name 17]: [ 17 start ]
[Name 5]: [ 5 start ]
[Name 10]: [ 10 start ]
[Name 22]: [ 22 start ]
[Name 4]: [ sleep 4 ]: done
[Name 5]: [ sleep 5 ]: done
[Name 6]: [ sleep 6 ]: done
------ process failed ------
[Name 10]: [ sleep 10 ]: done
done program
Explanations

The idea is to make the sub-processes notify the parent script when they fail (with a SIGUSR1 signal); the main script will then kill all the sub-processes when it receives that signal.
There's a problem though: killing the PID of a sub-process might not be enough, for example when it is currently running a command with a |. In those cases you need to kill the whole process group, which can be done by enabling job control with set -m and by using a negative PID in the kill command.

Fravadona
  • 5,892
  • 14
  • 26