Chapter 10Lesson 02~90 minutes

wait, Job Coordination, and Exit Status

Launching work in the background is easy. Correctly joining it back into the parent control flow is the real engineering task. `wait` is how asynchronous success and failure become visible again.

BeginnerProcesses & concurrencyHands-on lab

Learning objectives

By the end of this lesson

  • Wait for specific background jobs.
  • Capture exact asynchronous exit status.
  • Track multiple PIDs safely.
  • Associate PIDs with task identity.
  • Define aggregate failure behavior.

1. wait joins asynchronous work back into control flow

sleep 2 &
pid=$!
printf 'doing other work\n'
wait "$pid"
printf 'child complete\n'

wait blocks until the selected child completes and returns that child's status.

2. Preserve the exact child exit status

bash -c 'sleep 1; exit 7' &
pid=$!

if wait "$pid"; then
  status=0
else
  status=$?
fi

printf 'status=%d\n' "$status"

3. Store many PIDs in an array

pids=()

for delay in 1 2 3; do
  sleep "$delay" &
  pids+=("$!")
done

for pid in "${pids[@]}"; do
  wait "$pid"
done

4. Associate PIDs with task names

declare -A name_by_pid

run_task api &
name_by_pid[$!]=api

run_task worker &
name_by_pid[$!]=worker

for pid in "${!name_by_pid[@]}"; do
  name=${name_by_pid[$pid]}
  if wait "$pid"; then
    printf '%s=ok\n' "$name"
  else
    status=$?
    printf '%s=failed status=%d\n' "$name" "$status" >&2
  fi
done

5. Logical negation can hide the original status

# If exact status matters, prefer:
if wait "$pid"; then
  status=0
else
  status=$?
fi
Status detail

With a construct such as if ! wait "$pid"; then ..., the logical negation changes the immediate status seen by $?. Use an explicit success/else branch when you need the child's exact code.

6. Bare wait waits for outstanding children

job_a &
job_b &
job_c &
wait

This is concise when you only care that all outstanding jobs finish. It is less useful when task identity and individual statuses matter.

7. wait -n handles whichever child finishes next

task_a &
task_b &
task_c &

for _ in 1 2 3; do
  if wait -n; then
    printf 'one child succeeded\n'
  else
    printf 'one child failed\n' >&2
  fi
done

wait -n is Bash-specific and version-dependent, but it is valuable for bounded worker pools.

8. Concurrency needs an aggregate failure policy

PolicyBehaviorUse
Fail fastStop/cancel after first failureWhen later work is invalid
Wait for allCollect every resultFor diagnostic batches
Best effortContinue independent tasksFor partial-success workflows

9. Early parent exit must account for active children

If the parent exits before waiting, background children may remain active. The next lesson uses traps and signals to define cleanup and cancellation behavior.

10. Hands-on lab: coordinate three jobs

mkdir -p "$HOME/devops-academy/bash/chapter10/lesson02"
cd "$HOME/devops-academy/bash/chapter10/lesson02"

run_job() {
  local name=$1 delay=$2 result=$3
  sleep "$delay"
  printf '%s finished\n' "$name"
  return "$result"
}

declare -A name_by_pid
pids=()

run_job api 2 0 &
pids+=("$!")
name_by_pid[${pids[-1]}]=api

run_job worker 1 4 &
pids+=("$!")
name_by_pid[${pids[-1]}]=worker

run_job cache 3 0 &
pids+=("$!")
name_by_pid[${pids[-1]}]=cache

failed=0
for pid in "${pids[@]}"; do
  name=${name_by_pid[$pid]}
  if wait "$pid"; then
    printf 'RESULT %s=ok\n' "$name"
  else
    status=$?
    printf 'RESULT %s=failed status=%d\n' "$name" "$status" >&2
    ((failed += 1))
  fi
done

printf 'failed_jobs=%d\n' "$failed"
(( failed == 0 )) || exit 1

Verification checklist

11. Knowledge check

Question 1. What does wait PID return?

Question 2. Why store PIDs?

Question 3. When is bare wait not enough?

Question 4. Name three aggregate policies.

12. Summary

Background execution becomes reliable only when the parent tracks owned PIDs, waits for them, preserves their statuses, and applies an explicit aggregate failure policy.

13. Further reading

  • GNU Bash Reference Manual — wait and Job Control Builtins.
  • GNU Bash Reference Manual — Arrays.
  • POSIX wait utility.
  • Bash documentation for wait -n.
Next lesson

Signals and Graceful Termination

Continue Chapter 10 by making process lifecycle and concurrency behavior more explicit.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.