Claude on Question 14, Sep 11
I'm writing a bash script to purge orphaned children after the parent dies. What's the idiomatic way?Short answer
For the common case, trap EXIT and kill your tracked PIDs:
#!/usr/bin/env bash
set -euo pipefail
pids=()
spawn() { "$@" & pids+=("$!"); }
cleanup() {
trap - EXIT INT TERM # prevent re-entry
kill -TERM "${pids[@]}" 2>/dev/null || true
wait "${pids[@]}" 2>/dev/null || true
}
trap cleanup EXIT INT TERM
spawn sleep 300
spawn some_worker --flag
waitTracking $! explicitly is more predictable than kill $(jobs -p), though the latter is a fine one-liner for throwaway scripts:
trap 'kill $(jobs -p) 2>/dev/null' EXIT
Gotchas worth knowing
kill 0 is a footgun. It signals your whole process group. If your script was started by an interactive shell you get a fresh process group and it works fine; if it was invoked from another script, you share the caller's group and you'll take out your parent too.
Grandchildren escape. $! only gives you direct children. If those spawn their own subprocesses, signal the process group instead. To do that safely, give yourself a group to own:
if [[ -z ${_REEXEC:-} ]]; then
export _REEXEC=1
exec setsid --wait "$0" "$@"
fi
trap 'trap - EXIT; kill -- -$$ 2>/dev/null' EXITTradeoff: setsid detaches the controlling terminal, so Ctrl-C no longer reaches you — you have to forward signals yourself. Only reach for this if you actually have deep process trees.
EXIT doesn't fire on SIGKILL. This is the real orphan scenario. If the parent is kill -9ed or OOM-killed, no trap runs and the children reparent to init. Two options:
# Linux, util-linux ≥ 2.33: kernel sends SIGTERM when the parent dies, period setpriv --pdeathsig TERM -- some_worker &
Or push the problem down to the kernel's own bookkeeping:
systemd-run --user --scope --collect ./myscript.sh
A scope puts everything in a cgroup, and teardown kills all members regardless of how the parent died. This is the only genuinely airtight approach.
Add a grace period if children may ignore SIGTERM:
cleanup() {
trap - EXIT INT TERM
kill -TERM "${pids[@]}" 2>/dev/null || true
( sleep 5; kill -KILL "${pids[@]}" 2>/dev/null ) &
wait "${pids[@]}" 2>/dev/null || true
}Picking one
Explicit PID tracking plus an EXIT trap covers the large majority of scripts. Add setpriv --pdeathsig on the children if ungraceful parent death is a realistic failure mode, and go to a systemd scope or cgroup if orphans are genuinely unacceptable.
Exact recordShowHideThe model id, the run, the verbatim text with line numbers and its full sha256
- Scored
- answered
- Received
- 2026-09-11 09:09:12 UTC
- Run
- 20260911T090002Z-da0dd3
- Served by
- Claude Platform on AWS · HTTP 200 · finish stop
- Size
- 2,498 characters · 3,264 tokens out incl. hidden reasoning · 42.9 s
End of response · 75 lines · 2,498 characters · sha256 c358e9f048d2815623f11ace03b2d8eca72491cd11f46b4b90cda820d077c53c