Bash: Killing all processes in subprocess - linux

In bash I can get the process ID (pid) of the last subprocess through the $! variable. I can then kill this subprocess before it finishes:
(sleep 5) & pid=$!
kill -9 $pid
This works as advertised. If I now extend the subprocess with more commands after the sleep, the sleep command continues after the subprocess is killed, even though the other commands never get executed.
As an example, consider the following, which spins up a subprocess and monitor its assassination using ps:
# Start subprocess and get its pid
(sleep 5; echo done) & pid=$!
# grep for subprocess
echo "grep before kill:"
ps aux | grep "$pid\|sleep 5"
# Kill the subprocess
echo
echo "Killing process $pid"
kill -9 $pid
# grep for subprocess
echo
echo "grep after kill:"
ps aux | grep "$pid\|sleep 5"
# Wait for sleep to finish
sleep 6
# grep for subprocess
echo
echo "grep after sleep is finished:"
ps aux | grep "$pid\|sleep 5"
If I save this to a file named filename and run it, I get this printout:
grep before kill:
username 7464 <...> bash filename
username 7466 <...> sleep 5
username 7467 <...> grep 7464\|sleep 5
Killing process 7464
grep after kill:
username 7466 <...> sleep 5
username 7469 <...> grep 7464\|sleep 5
grep after sleep is finished:
username 7472 <...> grep 7464\|sleep 5
where unimportant information from the ps command is replaced with <...>. It looks like the kill has killed the overall bash execution of filename, while leaving sleep running.
How can I correctly kill the entire subprocess?

You can set a trap in the subshell to kill any active jobs before exiting:
(trap 'kill $(jobs -p)' EXIT; sleep 5; echo done ) & pid=$!

I don't know exactly why that sleep process gets orphaned, anyway instead kill you can use pkill with -P flag to also kill all children
pkill -TERM -P $pid
EDIT:
that means that in order to kill a process and all it's children you should use instead
CPIDS=`pgrep -P $pid` # gets pids of child processes
kill -9 $pid
for cpid in $CPIDS ; do kill -9 $cpid ; done

You can have a look at rkill that seems to meet your requirements :
http://www.unix.com/man-page/debian/1/rkill/
rkill [-SIG] pid/name...
When invoked as rkill, this utility does not display information about the processes, but
sends them all a signal instead. If not specified on the command line, a terminate
(SIGTERM) signal is sent.

Related

How to use "kill -STOP" in shell script?

I want to use "kill -STOP" in a script to wait for the process to end
I wrote this script, but "kill -STOP" command doesn't pause the process
#!/bin/bash
echo $$ > ~/screen_finished
screen -S app -dm bash -c "sleep 5 ; kill -CONT `cat ~/screen_finished`"
kill -STOP $$

How to kill a program by the hard way, only if it refuses to terminate the soft way?

How to exit a program by bash on a soft way and if this was not successful do it on a hard way.
Its known how to open a programm by bash and how to get the PID of the programm. The follow sample open the editor XED:
xed & PID=$!
Its known how to echo the PID of the program what was opened:
echo $PID
12345 # sample output of a PID
Its known how to try to close a program on a soft way and how to select the program by PID for this:
kill -15 $PID # select a program by PID and tray to kill it on a soft way
Its known how to close a program on a hard way and how to select the program by PID for this:
kill -9 $PID # select a program by PID and kill it on a hard way
Put all together what I have, a still missing something:
xed & PID=$! # open the program xed and get the pid
echo PID of programm: $PID
sleep 5 # lets run the xed 5 seconds
kill -15 $PID # select the xed by PID and try to kill it on soft way
# check based on variant 1, 2, 3 or other, the xed was sucessfull killed on soft way or not
# if the xed still running, kill it on follow way
kill -9 $PID # select the xed by PID and kill it on hard way
Based one Answer of Eddy763, the follow Logically better fitting variant:
xed & PID=$! # Start XED and get PID
sleep 2
# kill -15 $PID # Soft Kill, activate or deactivate for testing
echo
echo
sleep 2
if ! ps -p $PID
then
echo "The PID/program are closed successful by Soft Kill"
else
echo "The PID/program didn't be killed successful by Soft Kill active, so it will be killed now on hard way"
kill -9 $PID # Hard Kill
fi
echo
echo
You can get the information about the process still running on follow way:
xed & xedPid=$! # Start of editor XED and asking for PID
[1] 12345
kill -15 $PID
# getting the follow, if the XED was killed successfull soft before:
kill -15 $PID
bash: kill: (12345) - Kein passender Prozess gefunden
**[1]+ Beendet xed**
You can get the information about the process are still running or not, on follow way too:
xed & xedPid=$! # Start of editor XED and asking for PID
[1] 12345
# Output if xed are running
kill -0 $PID
kill: Aufruf: kill [-s Signalname | -n Signalnummer | -Signalname] pid | jobspec ... oder kill -l [Signalname]
kill -15 $PID
# Output if XED are not more running:
kill -0 $PID
kill: Aufruf: kill [-s Signalname | -n Signalnummer | -Signalname] pid | jobspec ... oder kill -l [Signalname]
**[1]+ Beendet xed**
kill -15 $PID
sleep 1
if ps -p $PID > /dev/null
then
kill -9 $PID
fi
#!/bin/bash
xed & PID=$! # Start XED and get PID
sleep 3
# kill -15 $PID # Soft Kill, activate or deactivate for testing
echo
echo
sleep 3
if ps -p $PID
then
echo "The PID/program didn't be killed successful by Soft Kill"
kill -9 $PID # Hard Kill
else
echo "PID/program are already closed by Soft Kill"
fi
echo
echo

how to kill process groups in trap?

In a bash script I normally use trap to clean up spawned processes:
function cleanup()
{
jobs -l
jobs -p | xargs -r -I {} kill -TERM {}
jobs -l
echo "do something after kill all jobs."
}
trap cleanup EXIT
However this does not work for process groups:
function cleanup()
{
jobs -l
jobs -p | xargs -r -I {} kill -TERM {}
jobs -l
echo "do something after kill all jobs."
}
trap cleanup EXIT
(sleep 100 | tee /tmp/sleep_test.log) | tee sleep_test2.log &
ps -ax -o pid,pgid,ppid,args | grep sleep
jobs -l
sleep 1
the jobs -p give out a ppid of process group of (sleep 100 | tee ...) and a process of tee ... The process group cannot be killed as above. It need to do kill -TERM -PGID. Is there any easy way to let jobs output process group PGID? Or is there any command can kill process group via PPID and process PID with a uniform interface?
update:
kill -TERM 0 does not work here since it kill itself also. But I still need to do something after kill all jobs.
The only way I found is killing sub-processes directly.
#!/usr/bin/env bash
function cleanup()
{
jobs -l
for p in $(jobs -p); do
kill $(pgrep -P $p)
done
jobs -l
echo "do something after kill all jobs."
}
trap cleanup EXIT
(sleep 100 | tee /tmp/sleep_test.log) | tee sleep_test2.log &
ps -ax -o pid,pgid,ppid,args | grep sleep
jobs -l
sleep 1
I tried to kill PGID, which didn't work for me.

Kill -2 or Kill -INT ,how does it kill process

I have two shellscripts:
a.sh is:
#!/bin/bash
read -p "Enter name": name
echo $name
b.sh is:
#!/bin/bash
for ((i=0; i<100; i++))
do
echo "$i"
sleep 1s
done
then, I start another shell using command pkill -2 a.sh pkill -2 b.sh
but, the first can be killed
the second cannot be killed,
what does pkill -2 do?
Kill -2 sends an interrupt (2 is the value associated with SIGINT). This will wake up the sleep call but then the loop continues. If you send a 15 (SIGTERM), the process should terminate.

Wait for arbitrary process and get its exit code in Linux

Is there a way to wait until a process finishes if I'm not the one who started it?
e.g. if I ran "ps -ef" and pick any PID (assuming I have rights to access process information) - is there a way I can wait until the PID completes and get its exit code?
You could use strace, which tracks signals and system calls. The following command waits until a program is done, then prints its exit code:
$ strace -e none -e exit_group -p $PID # process calls exit(1)
Process 23541 attached - interrupt to quit
exit_group(1) = ?
Process 23541 detached
$ strace -e none -e exit_group -p $PID # ^C at the keyboard
Process 22979 attached - interrupt to quit
--- SIGINT (Interrupt) # 0 (0) ---
Process 22979 detached
$ strace -e none -e exit_group -p $PID # kill -9 $PID
Process 22983 attached - interrupt to quit
+++ killed by SIGKILL +++
Signals from ^Z, fg and kill -USR1 get printed too. Either way, you'll need to use sed if you want to use the exit code in a shell script.
If that's too much shell code, you can use a program I hacked together in C a while back. It uses ptrace() to catch signals and exit codes of pids. (It has rough edges and may not work in all situations.)
I hope that helps!
is there a way I can wait until the PID completes and get its exit code
Yes, if the process is not being ptraced by somebody else, you can PTRACE_ATTACH to it, and get notified about various events (e.g. signals received), and about its exit.
Beware, this is quite complicated to handle properly.
If you can live without the exit code:
tail --pid=$pid -f /dev/null
If you know the process ID you can make use of the wait command which is a bash builtin:
wait PID
You can get the PID of the last command run in bash using $!. Or, you can grep for it with from the output of ps.
In fact, the wait command is a useful way to run parralel command in bash. Here's an example:
# Start the processes in parallel...
./script1.sh 1>/dev/null 2>&1 &
pid1=$!
./script2.sh 1>/dev/null 2>&1 &
pid2=$!
./script3.sh 1>/dev/null 2>&1 &
pid3=$!
./script4.sh 1>/dev/null 2>&1 &
pid4=$!
# Wait for processes to finish...
echo -ne "Commands sent... "
wait $pid1
err1=$?
wait $pid2
err2=$?
wait $pid3
err3=$?
wait $pid4
err4=$?
# Do something useful with the return codes...
if [ $err1 -eq 0 -a $err2 -eq 0 -a $err3 -eq 0 -a $err4 -eq 0 ]
then
echo "pass"
else
echo "fail"
fi

Resources