Reputation: 1154
I'd like to start a sequence of background job in a while loop in a bash script:
END=10
i=0;
while [ $i -lt $END ]
do
(SOME COMMAND;\
sleep 3;
SOME COMMAND 1;\
SOME COMMAND 2;\
SOME COMMAND 3;) &
i=`expr $i + 1`
done
And outside this while loop I'd like to wait until all these 10 background job all finished SOME COMMAND 1
and then proceed, so the psudo code is something like:
until [the number of process that finished SOME COMMAND 1 is greater or equal to $END];
do
sleep 0.5
done
What I tried to do is to add another counter like this:
true_started_cnt=0
END=10
i=0;
while [ $i -lt $END ]
do
(SOME COMMAND;\
sleep 3;
SOME COMMAND 1;\
true_started_cnt=`expr $true_started_cnt + 1`;\
SOME COMMAND 2;\
SOME COMMAND 3;) &
i=`expr $i + 1`
done
until [ $true_started_cnt -ge $END ]
do
echo "Waiting for initial period for all streams to finish"
sleep 0.5
done
But this seems not working, possibly because of simultaneous write of the same global var by multiple bg jobs. I wonder how to achieve my intention in this piece of code.
Referring to this post, I'm also trying:
echo 1 >/dev/shm/foo
END=10
i=0;
while [ $i -lt $END ]
do
(SOME COMMAND;\
sleep 3;
SOME COMMAND 1;\
echo $(($(</dev/shm/foo)+1)) >/dev/shm/foo;\
SOME COMMAND 2;\
SOME COMMAND 3;) &
i=`expr $i + 1`
done
until [ $(echo $(</dev/shm/foo)) -ge $END ]
do
echo "Waiting, true_started_cnt=$(echo $(</dev/shm/foo))"
sleep 1
done
But still not working.
Upvotes: 2
Views: 1505
Reputation: 179119
You could use a FIFO queue to signal completion of a certain stage of jobs:
#!/usr/bin/env bash
shopt -so errexit
shopt -so nounset
declare -ri job_count=5
# A FIFO queue where jobs will signal their completion.
mkfifo rendezvous
# Clean up FIFO queue on script exit.
trap EXIT EXIT; EXIT() {
rm -f rendezvous
trap - EXIT # Restore default interrupt handler.
kill -s EXIT "$$" # Propagate interrupt.
}
# Wait until the known number of jobs have signaled their completion to the
# FIFO queue.
wait_rendezvous() {
(
local -i counter=0
while read -r; do
(( ++counter ))
if (( counter == job_count )); then
break
fi
done
) < rendezvous
}
# Signal completion to the FIFO queue.
show_at_rendezvous() {
# For some reason, without the trailing ampersand some A jobs end up
# dangling.
echo "done" > rendezvous &
}
#
# Actual script logic starts below.
#
command_a() {
local -i job_id=$1
echo "Command A[$job_id]: start"
sleep 5
show_at_rendezvous
echo "Command A[$job_id]: done"
}
command_b() {
local -i job_id=$1
echo "Command B[$job_id]: start"
sleep 10
echo "Command B[$job_id]: done"
}
# Launch jobs.
for i in $(seq 1 "$job_count"); do
command_a "$i" &
command_b "$i" &
done
# Wait for all A jobs to signal their completion.
wait_rendezvous
echo "All A commands done; now awaiting for B commands..."
# Wait for all remaining jobs to finish.
wait
echo "All B commands done."
$ ./rendezvous.sh
Command A[1]: start
Command B[1]: start
Command A[2]: start
Command B[2]: start
Command A[3]: start
Command B[3]: start
Command A[4]: start
Command B[4]: start
Command A[5]: start
Command B[5]: start
Command A[2]: done
Command A[1]: done
All A commands done; now awaiting for B commands...
Command A[5]: done
Command A[3]: done
Command A[4]: done
Command B[1]: done
Command B[2]: done
Command B[3]: done
Command B[4]: done
Command B[5]: done
All B commands done.
Upvotes: 0
Reputation: 75478
Recording progress isn't needed if all you want is to make sure all processes exit.
#!/bin/bash
END=10
pids=()
for (( i = 0; i < END; ++i )); do
( sleep "$(( RANDOM % 10 + 1 ))" ) &
pids[$!]=$!
done
while [[ ${#pids[@]} -gt 0 ]]; do
for pid in "${pids[@]}"; do
echo "Waiting for $pid to exit."
wait "$pid"
kill -s 0 "$pid" >/dev/null || unset 'pids[pid]'
done
sleep 1
done
Upvotes: 0
Reputation: 26452
There could be race condition in your last script as well, try this :
cp /dev/null /dev/shm/foo
END=10
for ((i=0; i<$END; i++)); do
(
echo SOME COMMAND;\
sleep $((i+1));\
echo $i SOME COMMAND 1;\
echo $i >>/dev/shm/foo;\
echo $i SOME COMMAND 2;\
sleep $((i+1));\
echo $i SOME COMMAND 3
) &
done
until [ $(wc -l /dev/shm/foo | awk '{print $1}') -ge $END ]
do
echo "Waiting, /dev/shm/foo contains $(echo $(</dev/shm/foo))"
sleep 1
done
echo "*********** All SOME COMMAND 1 finished"
wait
echo "Script finished"
Upvotes: 1