4

My shell script is as following:

#!/bin/bash
./process1 #It will create a sub process: sub_process1
while [ condition ]; do
    break
done
kill -9 process1 and sub_process1

In my script, it will create a process: process1. The process1 will create a sub process: sub_process1.
Before the script finish, it need to kill the process1 and sub_process1.
It is easy to kill the process1 as it will write the PID into a file. But the sub_process1 will not. As the sub_process1 is a third-party component, I can't touch the source code.
There is a solution that can get the PID of sub_process1:

  1. Enumerate all processes with the command ps aux;
  2. Get PPID(parent process ID) for each process with the command ps -f [PID]. If the PPID is equal to the PID of process1, the process must be sub_process1.
    The above solution is a bit complicate. Is there a simple solution that can get sub process ID?
    Thanks a lot.
2

Since you tagged this as Linux: pgrep / pkill to the rescue:

PID_OF_SUB_PROCESS1=$( pgrep -P $PID_OF_PROCESS1 )
pkill -P $PID_OF_PROCESS1
0

What you could do is kill -TERM you process group. If your script is invoked from a shell, it'll have it's own process group equal to the script's PID:

kill -TERM -$$

If it is invoked otherwise, it's process group id might not be equal to its PID:

kill -TERM -`ps -o pgid $$ | tail -1`

You shouldn't kill -9 in a script or a program unless you give the program a fair chance with kill -TERM.

Doing a find pid and kill it is subject to race conditions (though they don't demonstrate frequently unless your box spawns processes like crazy). pids are a moving target and unless the pid you're killing is your child and you've ensured that pid won't get recycled by not waiting on it yet (something you can hardly do in bash as far as I know), you can't be 100% sure you're killing the right process (between finding a pid and killing, the process may have died and another one may have taken the pid).

  • You're grabbing a random process in the process group. (The one with the highest number, which is not necessarily the one started last, since process IDs wrap (on systems where they aren't randomized).) – Gilles May 22 '15 at 21:51
0

pkill can kill processes by name:

pkill process1 && pkill sub_process1

should do the job.

  • That's a very dangerous approach, because you don't know exactly what you're killing. For example, if the same user runs several instances of the script, they will all be killed. – Gilles May 23 '15 at 0:01
  • That's a good point. I assumed by the names process1 and sub_process1 that more than one instance wouldn't be run at a time, i.e. there exists a sub_process2, but it's not safe to run pkill bash, for example. – user1717828 May 23 '15 at 14:01
0

You can get the process ID of process by running it as a background process.

./process1 &
pid1=$!
wait "$pid1"

The wait command waits for process1 to exit (but not its subprocesses), like in your original script. Do note that in your original script, there's no process1 to kill at the end: the ./process1 command finishes only when process1 exits. It's possible that there is a child process of that process with the same name (i.e. the program may have called fork but not execve). If you want to continue running the script as soon as process1 itself has started, omit the wait line.

If you have the pkill command, it's a convenient way to kill all the children of a process. Note that the process must still be running, or must be a zombie, otherwise the children's parent process ID will be reset to 1 and you can't track them this way any longer. As long as you haven't called wait in your script, the background process ID will remain valid.

./process1 &
pid1=$!
…
pkill -9 -P "$pid1"
kill -9 "$pid1"

Another way to track a process, its children, and their children recursively is to track the process group. The children of a process have the same process group unless they explicitly change it. On Linux, you can use the setsid command to run a program in its own process group. The process group is identified by the process ID of the original process. To kill all the processes in a process group, pass the negative of the process group ID to kill.

setsid ./process1 &
pgid1=$!
…
kill -9 "-$pgid1"

Yet another way to track processes is to make them open a file. This works as long as the processes don't close the files, so it might not work for a program that's intended to run as a daemon. Use the command fuser to kill the processes that have the file open.

tmpfile=$(mktemp)
process1 <"$tmpfile"
…
fuser -k -9 "$tmpfile"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.