From bash, spawn two processes and exit both if either sibling exitsHost process for multiple processes?How to kill a script which starts new processes?Exiting a Bash script when a sudo child quitsExiting a shell script if certain child processes exitHow do I wait for a background process to complete?How do I start 3 processes on the same terminal, and then exit all 3 easily?Kill job in subshell with set -mHow to kill off these processes?run script started from nautilus on a open terminalHow can I reap a zombie process that is not a child of my shell?

Source permutation

Has a sovereign Communist government ever run, and conceded loss, on a fair election?

Dynamic Linkage of LocatorPane and InputField

Vocabulary for giving just numbers, not a full answer

Are all players supposed to be able to see each others' character sheets?

Can we track matter through time by looking at different depths in space?

How do we create new idioms and use them in a novel?

MySQL importing CSV files really slow

Having the player face themselves after the mid-game

School performs periodic password audits. Is my password compromised?

From an axiomatic set theoric approach why can we take uncountable unions?

Virginia employer terminated employee and wants signing bonus returned

Which situations would cause a company to ground or recall a aircraft series?

Does an unused member variable take up memory?

Why is there an extra space when I type "ls" in the Desktop directory?

Professor forcing me to attend a conference, I can't afford even with 50% funding

Which classes are needed to have access to every spell in the PHB?

NASA's RS-25 Engines

Shifting between bemols and diesis in the key signature

Can one live in the U.S. and not use a credit card?

How does Ehrenfest's theorem apply to the quantum harmonic oscillator?

Signed and unsigned numbers

Do cubics always have one real root?

What is better: yes / no radio, or simple checkbox?



From bash, spawn two processes and exit both if either sibling exits


Host process for multiple processes?How to kill a script which starts new processes?Exiting a Bash script when a sudo child quitsExiting a shell script if certain child processes exitHow do I wait for a background process to complete?How do I start 3 processes on the same terminal, and then exit all 3 easily?Kill job in subshell with set -mHow to kill off these processes?run script started from nautilus on a open terminalHow can I reap a zombie process that is not a child of my shell?













5















From bash, I am spawning two processes. These two processes depend on each other. I want both to exit if either one dies. What is the cleanest way to do that? Currently I have the following:



# start process a
/bin/program_a;
a_pid=$!

# start process b
/bin/program_b;
b_pid=$!

# kill process b if process a exits
wait $a_pid
echo "a_pid died, killing process b"
kill -9 $b_pid


But this only helps process b exit if process a dies. How to I also make process a exit if process b dies?










share|improve this question






















  • a third process checking the alive status of the mentioned 2 processes and killing the remaining process if one of them go away for any reason. You can build this function into both programs and have them poll each other and die gracefully if the other one disappears, but this is something your programs will have to do, while doing their actual jobs. Hence my suggestion about a separate process

    – MelBurslan
    Feb 2 '16 at 20:29











  • You can use kill -0 $PID to determine if a process is still alive. Knowing this, you can then: while /bin/true; do if ! kill -0 $pid_a; then kill -9 $pid_b; exit; fi; elif ! kill -0 $pid_b; then kill -9 $pid_a; exit; fi; done. That said, you really should not ever have to use kill -9; you should send SIGTERM rather than SIGKILL to allow the process to clean up after itself.

    – DopeGhoti
    Feb 2 '16 at 20:46












  • Use SIGCHLD. trap '"$@"' CHLD; job1&set kill "$!" && job2 &set "$@" "$!"; wait

    – mikeserv
    Feb 2 '16 at 20:47











  • @mikeserv, that would work with zsh, not bash.

    – Stéphane Chazelas
    Feb 2 '16 at 21:07











  • @StéphaneChazelas - seems like it should... Whatd i get wrong? I figured the wait was enough to hold it over to get both... Just cuffed it from a phone in a waiting room though... And if you say so i believe it... I am curious about why though...

    – mikeserv
    Feb 2 '16 at 21:09
















5















From bash, I am spawning two processes. These two processes depend on each other. I want both to exit if either one dies. What is the cleanest way to do that? Currently I have the following:



# start process a
/bin/program_a;
a_pid=$!

# start process b
/bin/program_b;
b_pid=$!

# kill process b if process a exits
wait $a_pid
echo "a_pid died, killing process b"
kill -9 $b_pid


But this only helps process b exit if process a dies. How to I also make process a exit if process b dies?










share|improve this question






















  • a third process checking the alive status of the mentioned 2 processes and killing the remaining process if one of them go away for any reason. You can build this function into both programs and have them poll each other and die gracefully if the other one disappears, but this is something your programs will have to do, while doing their actual jobs. Hence my suggestion about a separate process

    – MelBurslan
    Feb 2 '16 at 20:29











  • You can use kill -0 $PID to determine if a process is still alive. Knowing this, you can then: while /bin/true; do if ! kill -0 $pid_a; then kill -9 $pid_b; exit; fi; elif ! kill -0 $pid_b; then kill -9 $pid_a; exit; fi; done. That said, you really should not ever have to use kill -9; you should send SIGTERM rather than SIGKILL to allow the process to clean up after itself.

    – DopeGhoti
    Feb 2 '16 at 20:46












  • Use SIGCHLD. trap '"$@"' CHLD; job1&set kill "$!" && job2 &set "$@" "$!"; wait

    – mikeserv
    Feb 2 '16 at 20:47











  • @mikeserv, that would work with zsh, not bash.

    – Stéphane Chazelas
    Feb 2 '16 at 21:07











  • @StéphaneChazelas - seems like it should... Whatd i get wrong? I figured the wait was enough to hold it over to get both... Just cuffed it from a phone in a waiting room though... And if you say so i believe it... I am curious about why though...

    – mikeserv
    Feb 2 '16 at 21:09














5












5








5


2






From bash, I am spawning two processes. These two processes depend on each other. I want both to exit if either one dies. What is the cleanest way to do that? Currently I have the following:



# start process a
/bin/program_a;
a_pid=$!

# start process b
/bin/program_b;
b_pid=$!

# kill process b if process a exits
wait $a_pid
echo "a_pid died, killing process b"
kill -9 $b_pid


But this only helps process b exit if process a dies. How to I also make process a exit if process b dies?










share|improve this question














From bash, I am spawning two processes. These two processes depend on each other. I want both to exit if either one dies. What is the cleanest way to do that? Currently I have the following:



# start process a
/bin/program_a;
a_pid=$!

# start process b
/bin/program_b;
b_pid=$!

# kill process b if process a exits
wait $a_pid
echo "a_pid died, killing process b"
kill -9 $b_pid


But this only helps process b exit if process a dies. How to I also make process a exit if process b dies?







bash process






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Feb 2 '16 at 20:18









peskalpeskal

24719




24719












  • a third process checking the alive status of the mentioned 2 processes and killing the remaining process if one of them go away for any reason. You can build this function into both programs and have them poll each other and die gracefully if the other one disappears, but this is something your programs will have to do, while doing their actual jobs. Hence my suggestion about a separate process

    – MelBurslan
    Feb 2 '16 at 20:29











  • You can use kill -0 $PID to determine if a process is still alive. Knowing this, you can then: while /bin/true; do if ! kill -0 $pid_a; then kill -9 $pid_b; exit; fi; elif ! kill -0 $pid_b; then kill -9 $pid_a; exit; fi; done. That said, you really should not ever have to use kill -9; you should send SIGTERM rather than SIGKILL to allow the process to clean up after itself.

    – DopeGhoti
    Feb 2 '16 at 20:46












  • Use SIGCHLD. trap '"$@"' CHLD; job1&set kill "$!" && job2 &set "$@" "$!"; wait

    – mikeserv
    Feb 2 '16 at 20:47











  • @mikeserv, that would work with zsh, not bash.

    – Stéphane Chazelas
    Feb 2 '16 at 21:07











  • @StéphaneChazelas - seems like it should... Whatd i get wrong? I figured the wait was enough to hold it over to get both... Just cuffed it from a phone in a waiting room though... And if you say so i believe it... I am curious about why though...

    – mikeserv
    Feb 2 '16 at 21:09


















  • a third process checking the alive status of the mentioned 2 processes and killing the remaining process if one of them go away for any reason. You can build this function into both programs and have them poll each other and die gracefully if the other one disappears, but this is something your programs will have to do, while doing their actual jobs. Hence my suggestion about a separate process

    – MelBurslan
    Feb 2 '16 at 20:29











  • You can use kill -0 $PID to determine if a process is still alive. Knowing this, you can then: while /bin/true; do if ! kill -0 $pid_a; then kill -9 $pid_b; exit; fi; elif ! kill -0 $pid_b; then kill -9 $pid_a; exit; fi; done. That said, you really should not ever have to use kill -9; you should send SIGTERM rather than SIGKILL to allow the process to clean up after itself.

    – DopeGhoti
    Feb 2 '16 at 20:46












  • Use SIGCHLD. trap '"$@"' CHLD; job1&set kill "$!" && job2 &set "$@" "$!"; wait

    – mikeserv
    Feb 2 '16 at 20:47











  • @mikeserv, that would work with zsh, not bash.

    – Stéphane Chazelas
    Feb 2 '16 at 21:07











  • @StéphaneChazelas - seems like it should... Whatd i get wrong? I figured the wait was enough to hold it over to get both... Just cuffed it from a phone in a waiting room though... And if you say so i believe it... I am curious about why though...

    – mikeserv
    Feb 2 '16 at 21:09

















a third process checking the alive status of the mentioned 2 processes and killing the remaining process if one of them go away for any reason. You can build this function into both programs and have them poll each other and die gracefully if the other one disappears, but this is something your programs will have to do, while doing their actual jobs. Hence my suggestion about a separate process

– MelBurslan
Feb 2 '16 at 20:29





a third process checking the alive status of the mentioned 2 processes and killing the remaining process if one of them go away for any reason. You can build this function into both programs and have them poll each other and die gracefully if the other one disappears, but this is something your programs will have to do, while doing their actual jobs. Hence my suggestion about a separate process

– MelBurslan
Feb 2 '16 at 20:29













You can use kill -0 $PID to determine if a process is still alive. Knowing this, you can then: while /bin/true; do if ! kill -0 $pid_a; then kill -9 $pid_b; exit; fi; elif ! kill -0 $pid_b; then kill -9 $pid_a; exit; fi; done. That said, you really should not ever have to use kill -9; you should send SIGTERM rather than SIGKILL to allow the process to clean up after itself.

– DopeGhoti
Feb 2 '16 at 20:46






You can use kill -0 $PID to determine if a process is still alive. Knowing this, you can then: while /bin/true; do if ! kill -0 $pid_a; then kill -9 $pid_b; exit; fi; elif ! kill -0 $pid_b; then kill -9 $pid_a; exit; fi; done. That said, you really should not ever have to use kill -9; you should send SIGTERM rather than SIGKILL to allow the process to clean up after itself.

– DopeGhoti
Feb 2 '16 at 20:46














Use SIGCHLD. trap '"$@"' CHLD; job1&set kill "$!" && job2 &set "$@" "$!"; wait

– mikeserv
Feb 2 '16 at 20:47





Use SIGCHLD. trap '"$@"' CHLD; job1&set kill "$!" && job2 &set "$@" "$!"; wait

– mikeserv
Feb 2 '16 at 20:47













@mikeserv, that would work with zsh, not bash.

– Stéphane Chazelas
Feb 2 '16 at 21:07





@mikeserv, that would work with zsh, not bash.

– Stéphane Chazelas
Feb 2 '16 at 21:07













@StéphaneChazelas - seems like it should... Whatd i get wrong? I figured the wait was enough to hold it over to get both... Just cuffed it from a phone in a waiting room though... And if you say so i believe it... I am curious about why though...

– mikeserv
Feb 2 '16 at 21:09






@StéphaneChazelas - seems like it should... Whatd i get wrong? I figured the wait was enough to hold it over to get both... Just cuffed it from a phone in a waiting room though... And if you say so i believe it... I am curious about why though...

– mikeserv
Feb 2 '16 at 21:09











2 Answers
2






active

oldest

votes


















8














With zsh:



pids=()
trap '
trap - CHLD
(($#pids)) && kill $pids 2> /dev/null
' CHLD

sleep 2 & pids+=$!
sleep 1 & pids+=$!
sleep 3 & pids+=$!

wait


(here using sleep as test commands).



With bash it would seem the CHLD trap is only run when the m option is on. You don't want to start your jobs under that option though as that would run them in separate process groups. Also note that resetting the handler within the handler doesn't seem to work with bash. So the bash equivalent would be something like:



pids=()
gotsigchld=false
trap '
if ! "$gotsigchld"; then
gotsigchld=true
(($#pids[@])) && kill "$pids[@]" 2> /dev/null
fi
' CHLD

sleep 2 & pids+=("$!")
sleep 1 & pids+=("$!")
sleep 3 & pids+=("$!")

set -m
wait
set +m





share|improve this answer

























  • I was hesitant to use job control after reading stackoverflow.com/questions/690266/…, but this solution of turning job control on and off around the wait is perfect

    – peskal
    Feb 2 '16 at 21:46











  • @pesckal - you only need to worry about if your script ought to be backgroundable for the most part. If so, look at at.

    – mikeserv
    Feb 2 '16 at 22:34


















0














Of those I tested, and as near as I can tell, three shells do pretty much the right thing with regards to SIGCHLD and wait: yash, dash, and mksh. You see, wait is supposed to be interruptible; when setting up a signal handler you need that handler either to be doing a wait(), a sleep(), or a read() portably (though apparently sleep() might behave strangely if the interruption comes of a previous call to alarm()). Any (not blocked/ignored) signal should stop a wait().



The shell implementations of such things shouldn't differ terribly in my opinion, but... some do. Particularly bash behaves the worst of any of bash, ksh93, dash, mksh, yash, or zsh. zsh and ksh93 almost get the following sequence right, but they fail to preserve the exit status of the first process to exit. It's not terrible - though zsh does also complain about being asked to wait on the most recently exited pid anyway.



Here's what I did:



unset IFS
script=$(cat <<""
PS4="$0 + "
trap ' for p ### loop over bgd pids
do shift ### clear current pid
if kill -0 "$p" 2>/dev/null ### still running?
then set -- "$@" "$p" ### then append again
else wait "$p" ### else get return
exit "$(kill "$@")$?" ### kill others; exit
fi
done' CHLD ### wait til CHLD
for n in $(shuf -i 3-7) ### randomize order
do (sleep "$n";exit "$n")& set "$@" "$!" ### sleep 3 exits 3
done; set -x; wait ### debug, wait

)


The above should work not only to kill all remaining backgrounded children of a shell as soon as one returns, but also to propagate the first returned child's exit code to that of the parent shell. It should work because wait should return immediately with a backgrounded process's exit status if called for a child process which has not yet been waited upon. And because the SIGCHLD is what terminates the first wait the second wait should mark the first time the first returned child is actually waited. At least, simply put it should be. The more complicated the shell implementation, though, the less reliable such logic proves to be, it would seem.



That is the $script each of the shells ran when I did...



for sh in yash zsh ksh bash mksh dash
do time "$sh" +m -c "$script" ### no job control
done


bash is the only shell which does not exit within three seconds. zsh and ksh93 both (in my opinion, incorrectly) exit 0, but otherwise do quit within three seconds. The others exit 3 within 3 seconds. Here are the test results:



yash + wait
yash + shift
yash + wait 19111
yash + kill 19112 19113 19116 19117
yash + exit 3

real 0m3.013s
user 0m0.007s
sys 0m0.000s

zsh + wait
zsh + p=19124
zsh + shift
zsh + kill -0 19124
zsh + set -- 19125 19127 19129 19132 19124
zsh + p=19125
zsh + shift
zsh + kill -0 19125
zsh + wait 19125
zsh:wait:12: pid 19125 is not a child of this shell
zsh + kill 19127 19129 19132 19124
zsh + exit 0

real 0m3.023s
user 0m0.017s
sys 0m0.000s

ksh + wait
ksh + shift
ksh + kill -0 19137
ksh + 2> /dev/null
ksh + set -- 19138 19139 19140 19141 19137
ksh + shift
ksh + kill -0 19138
ksh + 2> /dev/null
ksh + wait 19138
ksh + kill 19139 19140 19141 19137
ksh + exit 0

real 0m3.018s
user 0m0.000s
sys 0m0.010s

bash + wait

real 0m7.018s
user 0m0.007s
sys 0m0.007s

mksh + wait
mksh + shift
mksh + 2>/dev/null
mksh + kill -0 19157
mksh + set -- 19158 19159 19160 19161 19157
mksh + shift
mksh + 2>/dev/null
mksh + kill -0 19158
mksh + set -- 19159 19160 19161 19157 19158
mksh + shift
mksh + 2>/dev/null
mksh + kill -0 19159
mksh + set -- 19160 19161 19157 19158 19159
mksh + shift
mksh + 2>/dev/null
mksh + kill -0 19160
mksh + set -- 19161 19157 19158 19159 19160
mksh + shift
mksh + 2>/dev/null
mksh + kill -0 19161
mksh + wait 19161
mksh + kill 19157 19158 19159 19160
mksh + exit 3

real 0m3.022s
user 0m0.003s
sys 0m0.000s

dash + wait
dash + shift
dash + kill -0 19165
dash + set -- 19166 19168 19170 19173 19165
dash + shift
dash + kill -0 19166
dash + wait 19166
dash + kill 19168 19170 19173 19165
dash + exit 3

real 0m3.008s
user 0m0.000s
sys 0m0.000s





share|improve this answer






















    Your Answer








    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "106"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader:
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    ,
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );













    draft saved

    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f259413%2ffrom-bash-spawn-two-processes-and-exit-both-if-either-sibling-exits%23new-answer', 'question_page');

    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    8














    With zsh:



    pids=()
    trap '
    trap - CHLD
    (($#pids)) && kill $pids 2> /dev/null
    ' CHLD

    sleep 2 & pids+=$!
    sleep 1 & pids+=$!
    sleep 3 & pids+=$!

    wait


    (here using sleep as test commands).



    With bash it would seem the CHLD trap is only run when the m option is on. You don't want to start your jobs under that option though as that would run them in separate process groups. Also note that resetting the handler within the handler doesn't seem to work with bash. So the bash equivalent would be something like:



    pids=()
    gotsigchld=false
    trap '
    if ! "$gotsigchld"; then
    gotsigchld=true
    (($#pids[@])) && kill "$pids[@]" 2> /dev/null
    fi
    ' CHLD

    sleep 2 & pids+=("$!")
    sleep 1 & pids+=("$!")
    sleep 3 & pids+=("$!")

    set -m
    wait
    set +m





    share|improve this answer

























    • I was hesitant to use job control after reading stackoverflow.com/questions/690266/…, but this solution of turning job control on and off around the wait is perfect

      – peskal
      Feb 2 '16 at 21:46











    • @pesckal - you only need to worry about if your script ought to be backgroundable for the most part. If so, look at at.

      – mikeserv
      Feb 2 '16 at 22:34















    8














    With zsh:



    pids=()
    trap '
    trap - CHLD
    (($#pids)) && kill $pids 2> /dev/null
    ' CHLD

    sleep 2 & pids+=$!
    sleep 1 & pids+=$!
    sleep 3 & pids+=$!

    wait


    (here using sleep as test commands).



    With bash it would seem the CHLD trap is only run when the m option is on. You don't want to start your jobs under that option though as that would run them in separate process groups. Also note that resetting the handler within the handler doesn't seem to work with bash. So the bash equivalent would be something like:



    pids=()
    gotsigchld=false
    trap '
    if ! "$gotsigchld"; then
    gotsigchld=true
    (($#pids[@])) && kill "$pids[@]" 2> /dev/null
    fi
    ' CHLD

    sleep 2 & pids+=("$!")
    sleep 1 & pids+=("$!")
    sleep 3 & pids+=("$!")

    set -m
    wait
    set +m





    share|improve this answer

























    • I was hesitant to use job control after reading stackoverflow.com/questions/690266/…, but this solution of turning job control on and off around the wait is perfect

      – peskal
      Feb 2 '16 at 21:46











    • @pesckal - you only need to worry about if your script ought to be backgroundable for the most part. If so, look at at.

      – mikeserv
      Feb 2 '16 at 22:34













    8












    8








    8







    With zsh:



    pids=()
    trap '
    trap - CHLD
    (($#pids)) && kill $pids 2> /dev/null
    ' CHLD

    sleep 2 & pids+=$!
    sleep 1 & pids+=$!
    sleep 3 & pids+=$!

    wait


    (here using sleep as test commands).



    With bash it would seem the CHLD trap is only run when the m option is on. You don't want to start your jobs under that option though as that would run them in separate process groups. Also note that resetting the handler within the handler doesn't seem to work with bash. So the bash equivalent would be something like:



    pids=()
    gotsigchld=false
    trap '
    if ! "$gotsigchld"; then
    gotsigchld=true
    (($#pids[@])) && kill "$pids[@]" 2> /dev/null
    fi
    ' CHLD

    sleep 2 & pids+=("$!")
    sleep 1 & pids+=("$!")
    sleep 3 & pids+=("$!")

    set -m
    wait
    set +m





    share|improve this answer















    With zsh:



    pids=()
    trap '
    trap - CHLD
    (($#pids)) && kill $pids 2> /dev/null
    ' CHLD

    sleep 2 & pids+=$!
    sleep 1 & pids+=$!
    sleep 3 & pids+=$!

    wait


    (here using sleep as test commands).



    With bash it would seem the CHLD trap is only run when the m option is on. You don't want to start your jobs under that option though as that would run them in separate process groups. Also note that resetting the handler within the handler doesn't seem to work with bash. So the bash equivalent would be something like:



    pids=()
    gotsigchld=false
    trap '
    if ! "$gotsigchld"; then
    gotsigchld=true
    (($#pids[@])) && kill "$pids[@]" 2> /dev/null
    fi
    ' CHLD

    sleep 2 & pids+=("$!")
    sleep 1 & pids+=("$!")
    sleep 3 & pids+=("$!")

    set -m
    wait
    set +m






    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited 1 hour ago

























    answered Feb 2 '16 at 21:31









    Stéphane ChazelasStéphane Chazelas

    309k57582942




    309k57582942












    • I was hesitant to use job control after reading stackoverflow.com/questions/690266/…, but this solution of turning job control on and off around the wait is perfect

      – peskal
      Feb 2 '16 at 21:46











    • @pesckal - you only need to worry about if your script ought to be backgroundable for the most part. If so, look at at.

      – mikeserv
      Feb 2 '16 at 22:34

















    • I was hesitant to use job control after reading stackoverflow.com/questions/690266/…, but this solution of turning job control on and off around the wait is perfect

      – peskal
      Feb 2 '16 at 21:46











    • @pesckal - you only need to worry about if your script ought to be backgroundable for the most part. If so, look at at.

      – mikeserv
      Feb 2 '16 at 22:34
















    I was hesitant to use job control after reading stackoverflow.com/questions/690266/…, but this solution of turning job control on and off around the wait is perfect

    – peskal
    Feb 2 '16 at 21:46





    I was hesitant to use job control after reading stackoverflow.com/questions/690266/…, but this solution of turning job control on and off around the wait is perfect

    – peskal
    Feb 2 '16 at 21:46













    @pesckal - you only need to worry about if your script ought to be backgroundable for the most part. If so, look at at.

    – mikeserv
    Feb 2 '16 at 22:34





    @pesckal - you only need to worry about if your script ought to be backgroundable for the most part. If so, look at at.

    – mikeserv
    Feb 2 '16 at 22:34













    0














    Of those I tested, and as near as I can tell, three shells do pretty much the right thing with regards to SIGCHLD and wait: yash, dash, and mksh. You see, wait is supposed to be interruptible; when setting up a signal handler you need that handler either to be doing a wait(), a sleep(), or a read() portably (though apparently sleep() might behave strangely if the interruption comes of a previous call to alarm()). Any (not blocked/ignored) signal should stop a wait().



    The shell implementations of such things shouldn't differ terribly in my opinion, but... some do. Particularly bash behaves the worst of any of bash, ksh93, dash, mksh, yash, or zsh. zsh and ksh93 almost get the following sequence right, but they fail to preserve the exit status of the first process to exit. It's not terrible - though zsh does also complain about being asked to wait on the most recently exited pid anyway.



    Here's what I did:



    unset IFS
    script=$(cat <<""
    PS4="$0 + "
    trap ' for p ### loop over bgd pids
    do shift ### clear current pid
    if kill -0 "$p" 2>/dev/null ### still running?
    then set -- "$@" "$p" ### then append again
    else wait "$p" ### else get return
    exit "$(kill "$@")$?" ### kill others; exit
    fi
    done' CHLD ### wait til CHLD
    for n in $(shuf -i 3-7) ### randomize order
    do (sleep "$n";exit "$n")& set "$@" "$!" ### sleep 3 exits 3
    done; set -x; wait ### debug, wait

    )


    The above should work not only to kill all remaining backgrounded children of a shell as soon as one returns, but also to propagate the first returned child's exit code to that of the parent shell. It should work because wait should return immediately with a backgrounded process's exit status if called for a child process which has not yet been waited upon. And because the SIGCHLD is what terminates the first wait the second wait should mark the first time the first returned child is actually waited. At least, simply put it should be. The more complicated the shell implementation, though, the less reliable such logic proves to be, it would seem.



    That is the $script each of the shells ran when I did...



    for sh in yash zsh ksh bash mksh dash
    do time "$sh" +m -c "$script" ### no job control
    done


    bash is the only shell which does not exit within three seconds. zsh and ksh93 both (in my opinion, incorrectly) exit 0, but otherwise do quit within three seconds. The others exit 3 within 3 seconds. Here are the test results:



    yash + wait
    yash + shift
    yash + wait 19111
    yash + kill 19112 19113 19116 19117
    yash + exit 3

    real 0m3.013s
    user 0m0.007s
    sys 0m0.000s

    zsh + wait
    zsh + p=19124
    zsh + shift
    zsh + kill -0 19124
    zsh + set -- 19125 19127 19129 19132 19124
    zsh + p=19125
    zsh + shift
    zsh + kill -0 19125
    zsh + wait 19125
    zsh:wait:12: pid 19125 is not a child of this shell
    zsh + kill 19127 19129 19132 19124
    zsh + exit 0

    real 0m3.023s
    user 0m0.017s
    sys 0m0.000s

    ksh + wait
    ksh + shift
    ksh + kill -0 19137
    ksh + 2> /dev/null
    ksh + set -- 19138 19139 19140 19141 19137
    ksh + shift
    ksh + kill -0 19138
    ksh + 2> /dev/null
    ksh + wait 19138
    ksh + kill 19139 19140 19141 19137
    ksh + exit 0

    real 0m3.018s
    user 0m0.000s
    sys 0m0.010s

    bash + wait

    real 0m7.018s
    user 0m0.007s
    sys 0m0.007s

    mksh + wait
    mksh + shift
    mksh + 2>/dev/null
    mksh + kill -0 19157
    mksh + set -- 19158 19159 19160 19161 19157
    mksh + shift
    mksh + 2>/dev/null
    mksh + kill -0 19158
    mksh + set -- 19159 19160 19161 19157 19158
    mksh + shift
    mksh + 2>/dev/null
    mksh + kill -0 19159
    mksh + set -- 19160 19161 19157 19158 19159
    mksh + shift
    mksh + 2>/dev/null
    mksh + kill -0 19160
    mksh + set -- 19161 19157 19158 19159 19160
    mksh + shift
    mksh + 2>/dev/null
    mksh + kill -0 19161
    mksh + wait 19161
    mksh + kill 19157 19158 19159 19160
    mksh + exit 3

    real 0m3.022s
    user 0m0.003s
    sys 0m0.000s

    dash + wait
    dash + shift
    dash + kill -0 19165
    dash + set -- 19166 19168 19170 19173 19165
    dash + shift
    dash + kill -0 19166
    dash + wait 19166
    dash + kill 19168 19170 19173 19165
    dash + exit 3

    real 0m3.008s
    user 0m0.000s
    sys 0m0.000s





    share|improve this answer



























      0














      Of those I tested, and as near as I can tell, three shells do pretty much the right thing with regards to SIGCHLD and wait: yash, dash, and mksh. You see, wait is supposed to be interruptible; when setting up a signal handler you need that handler either to be doing a wait(), a sleep(), or a read() portably (though apparently sleep() might behave strangely if the interruption comes of a previous call to alarm()). Any (not blocked/ignored) signal should stop a wait().



      The shell implementations of such things shouldn't differ terribly in my opinion, but... some do. Particularly bash behaves the worst of any of bash, ksh93, dash, mksh, yash, or zsh. zsh and ksh93 almost get the following sequence right, but they fail to preserve the exit status of the first process to exit. It's not terrible - though zsh does also complain about being asked to wait on the most recently exited pid anyway.



      Here's what I did:



      unset IFS
      script=$(cat <<""
      PS4="$0 + "
      trap ' for p ### loop over bgd pids
      do shift ### clear current pid
      if kill -0 "$p" 2>/dev/null ### still running?
      then set -- "$@" "$p" ### then append again
      else wait "$p" ### else get return
      exit "$(kill "$@")$?" ### kill others; exit
      fi
      done' CHLD ### wait til CHLD
      for n in $(shuf -i 3-7) ### randomize order
      do (sleep "$n";exit "$n")& set "$@" "$!" ### sleep 3 exits 3
      done; set -x; wait ### debug, wait

      )


      The above should work not only to kill all remaining backgrounded children of a shell as soon as one returns, but also to propagate the first returned child's exit code to that of the parent shell. It should work because wait should return immediately with a backgrounded process's exit status if called for a child process which has not yet been waited upon. And because the SIGCHLD is what terminates the first wait the second wait should mark the first time the first returned child is actually waited. At least, simply put it should be. The more complicated the shell implementation, though, the less reliable such logic proves to be, it would seem.



      That is the $script each of the shells ran when I did...



      for sh in yash zsh ksh bash mksh dash
      do time "$sh" +m -c "$script" ### no job control
      done


      bash is the only shell which does not exit within three seconds. zsh and ksh93 both (in my opinion, incorrectly) exit 0, but otherwise do quit within three seconds. The others exit 3 within 3 seconds. Here are the test results:



      yash + wait
      yash + shift
      yash + wait 19111
      yash + kill 19112 19113 19116 19117
      yash + exit 3

      real 0m3.013s
      user 0m0.007s
      sys 0m0.000s

      zsh + wait
      zsh + p=19124
      zsh + shift
      zsh + kill -0 19124
      zsh + set -- 19125 19127 19129 19132 19124
      zsh + p=19125
      zsh + shift
      zsh + kill -0 19125
      zsh + wait 19125
      zsh:wait:12: pid 19125 is not a child of this shell
      zsh + kill 19127 19129 19132 19124
      zsh + exit 0

      real 0m3.023s
      user 0m0.017s
      sys 0m0.000s

      ksh + wait
      ksh + shift
      ksh + kill -0 19137
      ksh + 2> /dev/null
      ksh + set -- 19138 19139 19140 19141 19137
      ksh + shift
      ksh + kill -0 19138
      ksh + 2> /dev/null
      ksh + wait 19138
      ksh + kill 19139 19140 19141 19137
      ksh + exit 0

      real 0m3.018s
      user 0m0.000s
      sys 0m0.010s

      bash + wait

      real 0m7.018s
      user 0m0.007s
      sys 0m0.007s

      mksh + wait
      mksh + shift
      mksh + 2>/dev/null
      mksh + kill -0 19157
      mksh + set -- 19158 19159 19160 19161 19157
      mksh + shift
      mksh + 2>/dev/null
      mksh + kill -0 19158
      mksh + set -- 19159 19160 19161 19157 19158
      mksh + shift
      mksh + 2>/dev/null
      mksh + kill -0 19159
      mksh + set -- 19160 19161 19157 19158 19159
      mksh + shift
      mksh + 2>/dev/null
      mksh + kill -0 19160
      mksh + set -- 19161 19157 19158 19159 19160
      mksh + shift
      mksh + 2>/dev/null
      mksh + kill -0 19161
      mksh + wait 19161
      mksh + kill 19157 19158 19159 19160
      mksh + exit 3

      real 0m3.022s
      user 0m0.003s
      sys 0m0.000s

      dash + wait
      dash + shift
      dash + kill -0 19165
      dash + set -- 19166 19168 19170 19173 19165
      dash + shift
      dash + kill -0 19166
      dash + wait 19166
      dash + kill 19168 19170 19173 19165
      dash + exit 3

      real 0m3.008s
      user 0m0.000s
      sys 0m0.000s





      share|improve this answer

























        0












        0








        0







        Of those I tested, and as near as I can tell, three shells do pretty much the right thing with regards to SIGCHLD and wait: yash, dash, and mksh. You see, wait is supposed to be interruptible; when setting up a signal handler you need that handler either to be doing a wait(), a sleep(), or a read() portably (though apparently sleep() might behave strangely if the interruption comes of a previous call to alarm()). Any (not blocked/ignored) signal should stop a wait().



        The shell implementations of such things shouldn't differ terribly in my opinion, but... some do. Particularly bash behaves the worst of any of bash, ksh93, dash, mksh, yash, or zsh. zsh and ksh93 almost get the following sequence right, but they fail to preserve the exit status of the first process to exit. It's not terrible - though zsh does also complain about being asked to wait on the most recently exited pid anyway.



        Here's what I did:



        unset IFS
        script=$(cat <<""
        PS4="$0 + "
        trap ' for p ### loop over bgd pids
        do shift ### clear current pid
        if kill -0 "$p" 2>/dev/null ### still running?
        then set -- "$@" "$p" ### then append again
        else wait "$p" ### else get return
        exit "$(kill "$@")$?" ### kill others; exit
        fi
        done' CHLD ### wait til CHLD
        for n in $(shuf -i 3-7) ### randomize order
        do (sleep "$n";exit "$n")& set "$@" "$!" ### sleep 3 exits 3
        done; set -x; wait ### debug, wait

        )


        The above should work not only to kill all remaining backgrounded children of a shell as soon as one returns, but also to propagate the first returned child's exit code to that of the parent shell. It should work because wait should return immediately with a backgrounded process's exit status if called for a child process which has not yet been waited upon. And because the SIGCHLD is what terminates the first wait the second wait should mark the first time the first returned child is actually waited. At least, simply put it should be. The more complicated the shell implementation, though, the less reliable such logic proves to be, it would seem.



        That is the $script each of the shells ran when I did...



        for sh in yash zsh ksh bash mksh dash
        do time "$sh" +m -c "$script" ### no job control
        done


        bash is the only shell which does not exit within three seconds. zsh and ksh93 both (in my opinion, incorrectly) exit 0, but otherwise do quit within three seconds. The others exit 3 within 3 seconds. Here are the test results:



        yash + wait
        yash + shift
        yash + wait 19111
        yash + kill 19112 19113 19116 19117
        yash + exit 3

        real 0m3.013s
        user 0m0.007s
        sys 0m0.000s

        zsh + wait
        zsh + p=19124
        zsh + shift
        zsh + kill -0 19124
        zsh + set -- 19125 19127 19129 19132 19124
        zsh + p=19125
        zsh + shift
        zsh + kill -0 19125
        zsh + wait 19125
        zsh:wait:12: pid 19125 is not a child of this shell
        zsh + kill 19127 19129 19132 19124
        zsh + exit 0

        real 0m3.023s
        user 0m0.017s
        sys 0m0.000s

        ksh + wait
        ksh + shift
        ksh + kill -0 19137
        ksh + 2> /dev/null
        ksh + set -- 19138 19139 19140 19141 19137
        ksh + shift
        ksh + kill -0 19138
        ksh + 2> /dev/null
        ksh + wait 19138
        ksh + kill 19139 19140 19141 19137
        ksh + exit 0

        real 0m3.018s
        user 0m0.000s
        sys 0m0.010s

        bash + wait

        real 0m7.018s
        user 0m0.007s
        sys 0m0.007s

        mksh + wait
        mksh + shift
        mksh + 2>/dev/null
        mksh + kill -0 19157
        mksh + set -- 19158 19159 19160 19161 19157
        mksh + shift
        mksh + 2>/dev/null
        mksh + kill -0 19158
        mksh + set -- 19159 19160 19161 19157 19158
        mksh + shift
        mksh + 2>/dev/null
        mksh + kill -0 19159
        mksh + set -- 19160 19161 19157 19158 19159
        mksh + shift
        mksh + 2>/dev/null
        mksh + kill -0 19160
        mksh + set -- 19161 19157 19158 19159 19160
        mksh + shift
        mksh + 2>/dev/null
        mksh + kill -0 19161
        mksh + wait 19161
        mksh + kill 19157 19158 19159 19160
        mksh + exit 3

        real 0m3.022s
        user 0m0.003s
        sys 0m0.000s

        dash + wait
        dash + shift
        dash + kill -0 19165
        dash + set -- 19166 19168 19170 19173 19165
        dash + shift
        dash + kill -0 19166
        dash + wait 19166
        dash + kill 19168 19170 19173 19165
        dash + exit 3

        real 0m3.008s
        user 0m0.000s
        sys 0m0.000s





        share|improve this answer













        Of those I tested, and as near as I can tell, three shells do pretty much the right thing with regards to SIGCHLD and wait: yash, dash, and mksh. You see, wait is supposed to be interruptible; when setting up a signal handler you need that handler either to be doing a wait(), a sleep(), or a read() portably (though apparently sleep() might behave strangely if the interruption comes of a previous call to alarm()). Any (not blocked/ignored) signal should stop a wait().



        The shell implementations of such things shouldn't differ terribly in my opinion, but... some do. Particularly bash behaves the worst of any of bash, ksh93, dash, mksh, yash, or zsh. zsh and ksh93 almost get the following sequence right, but they fail to preserve the exit status of the first process to exit. It's not terrible - though zsh does also complain about being asked to wait on the most recently exited pid anyway.



        Here's what I did:



        unset IFS
        script=$(cat <<""
        PS4="$0 + "
        trap ' for p ### loop over bgd pids
        do shift ### clear current pid
        if kill -0 "$p" 2>/dev/null ### still running?
        then set -- "$@" "$p" ### then append again
        else wait "$p" ### else get return
        exit "$(kill "$@")$?" ### kill others; exit
        fi
        done' CHLD ### wait til CHLD
        for n in $(shuf -i 3-7) ### randomize order
        do (sleep "$n";exit "$n")& set "$@" "$!" ### sleep 3 exits 3
        done; set -x; wait ### debug, wait

        )


        The above should work not only to kill all remaining backgrounded children of a shell as soon as one returns, but also to propagate the first returned child's exit code to that of the parent shell. It should work because wait should return immediately with a backgrounded process's exit status if called for a child process which has not yet been waited upon. And because the SIGCHLD is what terminates the first wait the second wait should mark the first time the first returned child is actually waited. At least, simply put it should be. The more complicated the shell implementation, though, the less reliable such logic proves to be, it would seem.



        That is the $script each of the shells ran when I did...



        for sh in yash zsh ksh bash mksh dash
        do time "$sh" +m -c "$script" ### no job control
        done


        bash is the only shell which does not exit within three seconds. zsh and ksh93 both (in my opinion, incorrectly) exit 0, but otherwise do quit within three seconds. The others exit 3 within 3 seconds. Here are the test results:



        yash + wait
        yash + shift
        yash + wait 19111
        yash + kill 19112 19113 19116 19117
        yash + exit 3

        real 0m3.013s
        user 0m0.007s
        sys 0m0.000s

        zsh + wait
        zsh + p=19124
        zsh + shift
        zsh + kill -0 19124
        zsh + set -- 19125 19127 19129 19132 19124
        zsh + p=19125
        zsh + shift
        zsh + kill -0 19125
        zsh + wait 19125
        zsh:wait:12: pid 19125 is not a child of this shell
        zsh + kill 19127 19129 19132 19124
        zsh + exit 0

        real 0m3.023s
        user 0m0.017s
        sys 0m0.000s

        ksh + wait
        ksh + shift
        ksh + kill -0 19137
        ksh + 2> /dev/null
        ksh + set -- 19138 19139 19140 19141 19137
        ksh + shift
        ksh + kill -0 19138
        ksh + 2> /dev/null
        ksh + wait 19138
        ksh + kill 19139 19140 19141 19137
        ksh + exit 0

        real 0m3.018s
        user 0m0.000s
        sys 0m0.010s

        bash + wait

        real 0m7.018s
        user 0m0.007s
        sys 0m0.007s

        mksh + wait
        mksh + shift
        mksh + 2>/dev/null
        mksh + kill -0 19157
        mksh + set -- 19158 19159 19160 19161 19157
        mksh + shift
        mksh + 2>/dev/null
        mksh + kill -0 19158
        mksh + set -- 19159 19160 19161 19157 19158
        mksh + shift
        mksh + 2>/dev/null
        mksh + kill -0 19159
        mksh + set -- 19160 19161 19157 19158 19159
        mksh + shift
        mksh + 2>/dev/null
        mksh + kill -0 19160
        mksh + set -- 19161 19157 19158 19159 19160
        mksh + shift
        mksh + 2>/dev/null
        mksh + kill -0 19161
        mksh + wait 19161
        mksh + kill 19157 19158 19159 19160
        mksh + exit 3

        real 0m3.022s
        user 0m0.003s
        sys 0m0.000s

        dash + wait
        dash + shift
        dash + kill -0 19165
        dash + set -- 19166 19168 19170 19173 19165
        dash + shift
        dash + kill -0 19166
        dash + wait 19166
        dash + kill 19168 19170 19173 19165
        dash + exit 3

        real 0m3.008s
        user 0m0.000s
        sys 0m0.000s






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Feb 3 '16 at 4:23









        mikeservmikeserv

        45.9k668160




        45.9k668160



























            draft saved

            draft discarded
















































            Thanks for contributing an answer to Unix & Linux Stack Exchange!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid


            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.

            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f259413%2ffrom-bash-spawn-two-processes-and-exit-both-if-either-sibling-exits%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            -bash, process

            Popular posts from this blog

            Mobil Contents History Mobil brands Former Mobil brands Lukoil transaction Mobil UK Mobil Australia Mobil New Zealand Mobil Greece Mobil in Japan Mobil in Canada Mobil Egypt See also References External links Navigation menuwww.mobil.com"Mobil Corporation"the original"Our Houston campus""Business & Finance: Socony-Vacuum Corp.""Popular Mechanics""Lubrite Technologies""Exxon Mobil campus 'clearly happening'""Toledo Blade - Google News Archive Search""The Lion and the Moose - How 2 Executives Pulled off the Biggest Merger Ever""ExxonMobil Press Release""Lubricants""Archived copy"the original"Mobil 1™ and Mobil Super™ motor oil and synthetic motor oil - Mobil™ Motor Oils""Mobil Delvac""Mobil Industrial website""The State of Competition in Gasoline Marketing: The Effects of Refiner Operations at Retail""Mobil Travel Guide to become Forbes Travel Guide""Hotel Rankings: Forbes Merges with Mobil"the original"Jamieson oil industry history""Mobil news""Caltex pumps for control""Watchdog blocks Caltex bid""Exxon Mobil sells service station network""Mobil Oil New Zealand Limited is New Zealand's oldest oil company, with predecessor companies having first established a presence in the country in 1896""ExxonMobil subsidiaries have a business history in New Zealand stretching back more than 120 years. We are involved in petroleum refining and distribution and the marketing of fuels, lubricants and chemical products""Archived copy"the original"Exxon Mobil to Sell Its Japanese Arm for $3.9 Billion""Gas station merger will end Esso and Mobil's long run in Japan""Esso moves to affiliate itself with PC Optimum, no longer Aeroplan, in loyalty point switch""Mobil brand of gas stations to launch in Canada after deal for 213 Loblaws-owned locations""Mobil Nears Completion of Rebranding 200 Loblaw Gas Stations""Learn about ExxonMobil's operations in Egypt""Petrol and Diesel Service Stations in Egypt - Mobil"Official websiteExxon Mobil corporate websiteMobil Industrial official websiteeeeeeeeDA04275022275790-40000 0001 0860 5061n82045453134887257134887257

            Frič See also Navigation menuinternal link

            Identify plant with long narrow paired leaves and reddish stems Planned maintenance scheduled April 17/18, 2019 at 00:00UTC (8:00pm US/Eastern) Announcing the arrival of Valued Associate #679: Cesar Manara Unicorn Meta Zoo #1: Why another podcast?What is this plant with long sharp leaves? Is it a weed?What is this 3ft high, stalky plant, with mid sized narrow leaves?What is this young shrub with opposite ovate, crenate leaves and reddish stems?What is this plant with large broad serrated leaves?Identify this upright branching weed with long leaves and reddish stemsPlease help me identify this bulbous plant with long, broad leaves and white flowersWhat is this small annual with narrow gray/green leaves and rust colored daisy-type flowers?What is this chilli plant?Does anyone know what type of chilli plant this is?Help identify this plant