#Sample task task(){ sleep 0.5; echo "$1"; }
#Sequential runs
for thing in a b c d e f g; do
task "$thing"
done
#Parallel runs for thing in a b c d e f g; do task "$thing" & done
#Parallel runs in N-process batches N=4 ( for thing in a b c d e f g; do ((i=i%N)); ((i++==0)) && wait task "$thing" & done )
It's also possible to use FIFOs as semaphores and use them to ensure that new processes are spawned as soon as possible and that no more than N processes runs at the same time. But it requires more code.
#N processes with a FIFO-based semaphore:
# initialize a semaphore with a given number of tokens
open_sem(){
mkfifo pipe-$$
exec 3<>pipe-$$
rm pipe-$$
local i=$1
for((;i>0;i--)); do
printf %s 000 >&3
done
}
# run the given command asynchronously and pop/push tokens
run_with_lock(){
local x
# this read waits until there is something to read
read -u 3 -n 3 x && ((0==x)) || exit $x
(
( "$@"; )
# push the return code of the command to the semaphore
printf '%.3d' $? >&3
)&
}
N=4
open_sem $N
for thing in {a..g}; do
run_with_lock task $thing
done
Explanation:
We use file descriptor 3 as a semaphore by pushing (=printf) and poping (=read) tokens ('000'). By pushing the return code of the executed tasks, we can abort if something went wrong.