1

I have a script which starts 3 terminal emulator windows:

#!/bin/sh

terminator --role='terminator-left' 2>/dev/null &
sleep 0.1
terminator --role='terminator-center' 2>/dev/null &
sleep 0.1
terminator --role='terminator-right' 2>/dev/null &

(I am using the --role option, so that I can automatically place the windows on different monitors, defined in my openbox configuration file)

I start this script multiple times, on different virtual desktops.

How can I start these 3 terminals in some kind of a "process group", so that I can then kill all of the remaining processes, in case any one of the processes terminates?

Important: I don't want to kill all terminals that might have been launched in a different instance of my script (I cannot just use pkill or pgrep to kill any process matching a pattern)

So, in other words, lets say I launch my script on virtual desktop 1 and virtual desktop 2.

I have now 3 terminal windows on 2 different virtual desktops.

When I exit one terminal on virtual desktop 1, I would like the remaining 2 windows to automatically terminate, but without affecting the independent instance of 3 other windows running on my virtual desktop 2.

Is what I describe possible ?

How can I achieve this ?

2
  • Do you have the bash shell?
    – meuh
    Commented Jan 21 at 18:20
  • @meuh - I do have bash installed, but I am using zsh Commented Jan 22 at 5:04

3 Answers 3

3

Disclaimer :
I have had no opportunity to test my suggestion under openbox. I validated it under kde-plasma only,
My solution resorts on the wait shell internal command as documented in the POSIX Programmers manual. Some particular implementation could make the script simpler, using the -n option.


#!/bin/sh

ownkill()
{
        trap - CHLD
        pkill --signal 15 -P $$ 
}

terminator --role='terminator-left' 2>/dev/null &
sleep 0.1
terminator --role='terminator-center' 2>/dev/null &
sleep 0.1
terminator --role='terminator-right' 2>/dev/null &
trap ownkill CHLD
wait

This script is based on the idea to :

  1. Launch the 3 terminator processes as desired,
  2. Await for their completion,
  3. Handle the SIGCHLD signalling the termination of one of the above processes in a way to terminate the remaining ones.

Nota 1 (reseting the handler) : As rightfully observed by aviro in comments, the signal handler must reset the handling of SIGCHLD to defaults since the killing of the remaining processes will trigger other (up to 2) SIGCHLD.
Failing to do so would force the shell in some sort of recursive situation (the handler having to handle a signal it generated itself ) more or less gracefully managed depending on shell implementations. (from a harmless warning to some maximum recursion depth exceeded or equiv. error.)

Nota 2 (Keep SIGCHLD default handling until wait) : As rightfully observed by Martin Vegter in comments, the trap ownkill CHLD instruction is to be placed immediately before the wait instruction.
Placing it as the first instruction of the script (as I had absurdly suggested in a preceding version) would trigger the dedicated handler as soon as the first sleep process terminates.

8
  • 1
    In my case, Job Control is disabled in scripts so trapping CHLD won't work. For my trapping CHLD works only if I set "Monitor mode" with set -m or set -o monitor. Not sure how it works for you. Anyway, the warning is shown because the pkill command also triggers a SIGCHLD when it ends. You'll need to reset the trap for CHLD inside the ownkill function before pkill.
    – aviro
    Commented Jan 22 at 14:25
  • 1
    @MC68020 - thank you, but it does not work!. I get: Maximum function recursion depth (1000) reached Commented Jan 22 at 14:44
  • @aviro : You are absolutely correct regarding the need to reset trap handling of CHLD inside the local handler. I stand corrected. Thanks. Still investigating about job control related issues though. Does not seem causing any trouble in my case.
    – MC68020
    Commented Jan 22 at 15:02
  • 1
    @MC68020 - now after the edit, I no longer get the "recursion reached" error. However, for the script to work as intended, I had to move the trap ownkill CHLD to the end, before wait. Is that what you intended ? Commented Jan 23 at 4:44
  • 1
    @MC68020 - so with the change mentioned above, the script seems to work exactly as I wanted. I just don't understand the logic why it works, and how you intended it to work in your version of the script. I would be happy to accept your answer, if you could briefly explain the logic. thank you Commented Jan 23 at 7:14
2

Using bash, you can simply collect all the process ids, and wait at the end so that when any process exits (bash's wait -n), then all the ids are killed with some suitable signal:

#!/bin/bash
terminator --role='terminator-left' 2>/dev/null &
pid1=$!
sleep 0.1
terminator --role='terminator-center' 2>/dev/null &
pid2=$!
sleep 0.1
terminator --role='terminator-right' 2>/dev/null &
pid3=$!
wait -n
kill -hup $pid1 $pid2 $pid3 2>/dev/null

As one of the pids will already be dead, we redirect the kill stderr to null to avoid an error message.

1

You might be open to using a systemd transient unit, as it is simple to start, control, and stop. Assuming your shell script is, say, ./myscript, add a wait -n command to the end of it so that is does not finish immediately, then you can start it with:

systemd-run --user --unit=mytest1 ./myscript

This command returns immediately with the message Running as unit: mytest1.service, and you can see what processes are running in the created transient unit with the usual commands:

systemctl status --user mytest1

You can stop all the processes with one command:

systemctl stop --user mytest1

To run the same script several times, just ensure you use a different --unit= name each time. If they all begin with test, then you can check on them all, or stop them all by referring to them as test*.

See man systemd-run for various options that might be useful, such as --setenv=, --working-directory=, --collect, --wait. Note that you can also pass arguments to your script at the end of the systemd-run command.

5
  • Sorry but I fail to understand how the termination of one process would consequently trigger the termination of others of the same unit.
    – MC68020
    Commented Jan 21 at 16:33
  • I meant to say add a wait -n, not just a wait. This wait will return when any background process exits. This makes the script finish, and systemd kills the other processes in the control group as the unit stops.
    – meuh
    Commented Jan 21 at 17:02
  • @meuh - sorry, but I am not using systemd. I am using sysvinit. I did not specify this in my question, because I did not anticipate it would be relevant. Commented Jan 21 at 17:18
  • @meuh… is'nt wait -n exclusively bash ? (When apparently OP's scripts is sh ?)
    – MC68020
    Commented Jan 21 at 17:19
  • @MC68020 yes, wait -n is not in all shells, but some systems link sh to bash and that accepts wait -n, and otherwise bash is often available in distributions.
    – meuh
    Commented Jan 21 at 18:00

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .