Let's go through your options.
Wait for all background jobs, unconditionally
for i in 1 2 3 4 5; do
cmd &
done
wait
This has the benefit of being simple, but you can't keep your machine busy. If you want to start new jobs as old ones complete, you can't. You machine gets less and less utilized until all the background jobs complete, at which point you can start a new batch of jobs.
Related is the ability to wait for a subset of jobs by passing multiple arguments to wait
:
unrelated_job &
for i in 1 2 3 4 5; do
cmd & pids+=($!)
done
wait "${pids[@]}" # Does not wait for unrelated_job, though
Wait for individual jobs in arbitrary order
for i in 1 2 3 4 5; do
cmd & pids+=($!)
done
for pid in "${pids[@]}"; do
wait "$pid"
# do something when a job completes
done
This has the benefit of letting you do work after a job completes, but
still has the problem that jobs other than $pid
might complete first, leaving your machine underutilized until $pid
actually completes. You do, however, still get the exit status for each individual job, even if it completes before you actually wait for it.
Wait for the next job to complete (bash
4.3 or later)
for i in 1 2 3 4 5; do
cmd & pids+=($!)
done
for pid in "${pids[@]}"; do
wait -n
# do something when a job completes
done
Here, you can wait until a job completes, which means you can keep your machine as busy as possible. The only problem is, you don't necessarily know which job completed, without using jobs
to get the list of active processes and comparing it to pids
.
Other options?
The shell by itself is not an ideal platform for doing job distribution, which is why there are a multitude of programs designed for managing batch jobs: xargs
, parallel
, slurm
, qsub
, etc.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…