Home > Mobile >  How to kill all background processes in a shell with CONTROL C
How to kill all background processes in a shell with CONTROL C

Time:06-22

I'm new to shell scripting. I have the following task to solve: I need to write a script that works with zsh and bash, where I can run 8 given scripts parallel.

The script that I wrote does the job, but once the script is running I can't stop these 8 subscripts properly. So my question is: How can I achieve that when I quit my script with CRTL C, all running subscripts get killed as well?

My Script:

#!/bin/bash


echo "This runs 8 similar versions of giza"

cd giza_google
sh runAgent.sh &
cd ..

cd giza
sh runAgent.sh &
cd ..

cd giza_1
sh runAgent.sh &
cd ..

cd giza_2
sh runAgent.sh &
cd ..

cd giza_3
sh runAgent.sh &
cd ..

cd giza_4
sh runAgent.sh &
cd ..

cd giza_5
sh runAgent.sh &
cd ..

cd giza_6
sh runAgent.sh 
cd ..

Context: "giza" is an agent name and the directory structure should not be changed. Each folder contains a runAgent.sh that starts a simulation agent.

What I have already tried:

trap 'kill $(jobs -p)' EXIT

trap "exit" INT TERM ERR
trap "kill 0" EXIT

#!/bin/bash


trap ctrl_c INT

function ctrl_c() {
    echo "Trapped CTRL_C"
    kill -KILL $PID1 $PID2 $PID3 $PID4 $PID5 $PID6 $PID7 
}

echo "This runs 8 similar versions of giza"

cd giza_google
sh runAgent.sh &
PID1=$!
echo "PID1=" $PID1
cd ..

cd giza
sh runAgent.sh &
PID2=$!
echo "PID2=" $PID2
cd ..

cd giza_1
sh runAgent.sh &
PID3=$!
echo "PID3=" $PID3
cd ..

cd giza_2
sh runAgent.sh &
PID4=$!
echo "PID4=" $PID4
cd ..

cd giza_3
sh runAgent.sh &
PID5=$!
echo "PID5=" $PID5
cd ..

cd giza_4
sh runAgent.sh &
PID6=$!
echo "PID6=" $PID6
cd ..

cd giza_5
sh runAgent.sh &
PID7=$!
echo "PID7=" $PID7
cd ..

cd giza_6
sh runAgent.sh 
cd ..

CodePudding user response:

Just negate the pgid when you specify the pid to kill. You should be able to use $$ as the process group, but sometimes it's convenient to pull the process group id from ps. Try:

#!/bin/bash

echo "This runs 8 similar versions of giza"

pgid=$(ps -o pgid= $$ | tr -d ' ')
trap 'kill -- -$pgid' 0

for d in giza_google giza giza_{1..6}; do
        (cd $d; sh runAgent) &
done
wait

Note that I've changed the name of the script to runAgent without the .sh suffix. See https://www.talisman.org/~erlkonig/documents/commandname-extensions-considered-harmful/

CodePudding user response:

Insofar as you're using bash, arrays are available to you. "Append to this array" syntax doesn't require all the manual control over numbering involved in having a separate array per child, and you can expand your complete array's contents onto a kill command when you want to shut everything down.

#!/usr/bin/env bash
agent_pids=( )

(cd giza_a && exec ./runAgent) & agent_pids =( "$!" )
(cd giza_b && exec ./runAgent) & agent_pids =( "$!" )
(cd giza_c && exec ./runAgent) & agent_pids =( "$!" )

wait "${agent_pids[@]}" # let them all finish
kill "${agent_pids[@]}" # shut them all down

Using exec consumes the subshell that the cd was scoped to, and ensures that the PID you're storing is actually that of runAgent, not the subshell that started it.

Of course, you can do all this in a function:

#!/usr/bin/env bash
shutdown() { kill "${agent_pids[@]}"; }
trap shutdown EXIT

agent_pids=( )

start_agent() {
  for dir; do
    (cd "$dir" && exec ./runAgent) & agent_pids =( "$!" )
  done
}

start_agent giza_a giza_b giza_c

...and you can even use an associative array so you build a map from directories to their associated agents:

#!/usr/bin/env bash

declare -A agent_pids=( )
shutdown() { kill "${agent_pids[@]}"; }
trap shutdown EXIT

start_agent() {
  for dir; do
    (cd "$dir" && exec ./runAgent) & agent_pids[$dir]=$!
  done
}

start_agent giza_a giza_b giza_c

for agent_name in "${!agent_pids[@]}"; do
  agent_pid=${agent_pids[$agent_name]}
  wait "$agent_pid"; agent_rc=$?
  echo "Agent $agent_name with PID $agent_pid exited with status $agent_rc"
done

CodePudding user response:

You might consider using GNU Parallel to run jobs in parallel. Your whole script becomes:

parallel 'cd {} && ./runAgent' ::: giza*/ 
  • Related