Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

capture pid of terminated background process using trap in bash

I'm writing a bash script that spawns a number of background processes. I'd like to be notified whenever a process terminates; and I would like to know the pid of the terminated process.

I have looked at using wait, such that the pids of each spawned process are captured and stored in an array, and then the array is iterated over, and wait is called on each process. This technique is described in various ways here:

https://stackoverflow.com/a/356154/366856

The problem with this, if I understand it correctly, is that it can be relatively inefficient if you want to perform some action as soon as an individual process terminates. For example, if you spawn processes A and B, and B finishes before A, then execution must wait for both A and B to finish. I need to perform an action as soon as B finishes.

It seems trap SIGCHLD is the way to perform an action immediately when a child terminates, but the problem is that I cannot find a way to determine the pid of the the source of the SIGCHLD signal, which I also need. The closest thing I've found to a working example of this is the third code example in this post:

http://www.linuxforums.org/forum/programming-scripting/103996-multithreading-bash.html#post510187

I've copy-pasted it here for convenience:

#!/bin/bash

iNext=0

function ChildReturned() {
        wait $1
        echo "$1 was returned. Its exits status was: $?"
        iNext=$((iNext+1))
        StartChild $iNext
}


function StartChild {
        ./child $1 &
        pid=$!
        echo "Started: $1  PID= $pid"
        trap 'ChildReturned $pid' SIGCHLD
}


set -bm

for (( iNext=0; iNext<=10; iNext++ ));
do
        StartChild $iNext
done

wait

I think this example doesn't work, because the author makes the mistake of calling trap multiple times, each time capturing a different pid variable in trap's argument. Each time trap is called, it overwrites the previous call, and thus an arbitrary pid will be captured. I haven't been able to find a better technique.

Is there a way to get the pid of a terminated child process using trap in bash?

Update

I just wanted to point to this resource, which contains the best, most comprehensive overview of solutions to this problem that I have seen thus far:

http://mywiki.wooledge.org/ProcessManagement#I_want_to_process_a_bunch_of_files_in_parallel.2C_and_when_one_finishes.2C_I_want_to_start_the_next._And_I_want_to_make_sure_there_are_exactly_5_jobs_running_at_a_time.

like image 591
jbeard4 Avatar asked Mar 02 '12 16:03

jbeard4


1 Answers

Set the trap once, and in the trap you can call jobs -n to see which children have finished since you last checked. Note that this doesn't tell you which child sent the SIGCHLD; consider the case where two children die before you enter the trap. In that case, you will only enter the trap once, and it is a moot point which child's signal you are actually responding to. Note that -n is a bash extension, and you might prefer to use jobs -l.

like image 104
William Pursell Avatar answered Oct 17 '22 16:10

William Pursell