views:

46

answers:

2

how to a Write a script that runs a 5 instances of a child process in the background (at a gap of 5 seconds) and do a continuous check to see if any of the child process is completed it starts another instance of the same process till the child process has executed 20 instances.

+2  A: 

Well I'm not entirely sure what you're asking but I'll do my best to guide you on the right path...

you can fork processes in your script like so:

/path/to/executable &

wait 5 seconds:

sleep 5

you can see how many instances of your program are currently running via ps:

ps aux | grep executable | wc -l

To keep track of how many instances have run, use a loop and an incrementing value:

#!/bin/bash
count = 0
while [ $count -lt 20 ]
do
    if [ `ps aux | grep executable | wc -l` -lt 5 ]
    then
        /path/to/executable &
        let "count += 1"
        sleep 5
    fi
done

the description is somewhat vague, I'm not sure if you want the 5 second gap to carry on for the subsequent 15 instances or not, and if you only want a max of 5 running at a time? That's how I interpreted it. There's many ways to implement this logic.

John T
Should work well as long as there are no other running `executable` processes. Also, unlike my version, it has a chance of working if the target program double-forks itself away from the initial launch PID.
Chris Johnsen
A: 

This should run in most POSIX-ish shells (I tested with dash (956b4bd… from Git), ksh (“1993-12-28 p” from Mac OS X 10.4), bash (2.05b and 4.0.35), zsh (4.3.10-dev-1; actually 8a04e94… from Git)):

#!/usr/bin/env dash

# usage: run-em <num> <num-simul> <start-gap> <executable> [<args ...]

test -n "$ZSH_VERSION" && setopt sh_word_split

pids=''
num="$1"
num_started=0
num_simul="$2"
start_gap="$3"
shift 3

poll_gap=1 # polling interval

while test "$num_started" -lt "$num" ||
      test -n "$pids"; do
    alive_pids=''
    for pid in $pids; do
        if kill -0 "$pid" > /dev/null 2>&1; then
            alive_pids="$pid
$alive_pids"
        else
            printf 'PID %s is gone\n' "$pid" 1>&2
        fi
    done
    pids="$alive_pids"

    new_pid=''
    if test "$num_started" -lt "$num" &&
       test "$(printf "$pids" | wc -l)" -lt "$num_simul"; then
        exec 3>&1
        new_pid="$("$@" 1>&3 3>&- & echo "$!")"
        exec 3>&-
        num_started="$((num_started+1))"
        printf 'Started PID %s as #%s\n' "$new_pid" "$num_started" 1>&2
        pids="$new_pid
$pids"
        if test -n "$start_gap" &&
           test "$num" -gt 0; then
            printf 'Waiting %s seconds...' "$start_gap" 1>&2
            sleep "$start_gap"
            printf 'done\n' 1>&2
        fi
    fi
    test -z "$start_gap$new_pid" && sleep "$poll_gap"
    test "$num_started" -ge "$num_simul" && start_gap=''
done

Tested with: run-em 5 2 4 sh -c 'echo "[[$$ starting]]";sleep 13;echo "[[$$ ending]]"'

If you wanted to tie yourself to a particular shell, you might be able to use job control and a trap on SIGCHLD instead of double forking and probing with kill -0 (ksh and zsh seem capable of this, dash and bash seem broken for this; zsh also has a jobstates parameter that could be used to check for running jobs if SIGCHLD traps happened to be broken there). In some languages (e.g. Perl), you can use waitpid(2) or wait4(2) to wait for any child process so that you could collect their actual exit codes. Unfortunately, most shells seem to only let you wait for a specific child process, which would only work if you knew ahead of time which process was going to finish first.

Chris Johnsen