GPT on Question 14, Sep 24
I'm writing a bash script to purge orphaned children after the parent dies. What's the idiomatic way?A Bash process cannot clean up after it has died. The usual approach is:
- 1.Handle normal exit and catchable signals with
trap. - 2.Put each child workload in its own process group/session.
- 3.Kill the whole group during cleanup.
#!/usr/bin/env bash
children=()
run() {
setsid -- "$@" &
children+=("$!") # also the new process-group ID
}
cleanup() {
status=$?
trap - EXIT HUP INT TERM
# Ask each child process group to terminate.
for pgid in "${children[@]}"; do
kill -TERM -- "-$pgid" 2>/dev/null || :
done
sleep 1
# Forcefully remove anything still alive.
for pgid in "${children[@]}"; do
kill -KILL -- "-$pgid" 2>/dev/null || :
done
wait 2>/dev/null || :
exit "$status"
}
trap cleanup EXIT
trap 'exit 129' HUP
trap 'exit 130' INT
trap 'exit 143' TERM
run command1 arg1
run command2 arg2
waitThe negative PID in kill -- "-$pgid" means “signal the entire process group,” so grandchildren are included unless they deliberately create another session/process group.
Important limitations:
- •Traps do not run for
SIGKILL, kernel crashes, or abrupt power loss. - •
pkill -P $$andjobs -ponly reliably find direct children and are race-prone. - •Avoid
kill 0unless the script is guaranteed to have its own process group; otherwise it may kill the invoking shell too.
For hard guarantees, run the script under a supervisor using a cgroup—e.g. systemd with its default KillMode=control-group. On Linux, another option is making each child set prctl(PR_SET_PDEATHSIG, ...), though Bash cannot do that natively.
Exact recordShowHideThe model id, the run, the verbatim text with line numbers and its full sha256
- Scored
- answered
- Received
- 2026-09-24 09:01:48 UTC
- Run
- 20260924T090023Z-506a29
- Served by
- OpenAI · HTTP 200 · finish stop
- Size
- 1,625 characters · 1,863 tokens out incl. hidden reasoning · 29.6 s
A Bash process cannot clean up **after it has died**. The usual approach is:
1. Handle normal exit and catchable signals with `trap`.
2. Put each child workload in its own process group/session.
3. Kill the whole group during cleanup.
```bash
#!/usr/bin/env bash
children=()
run() {
setsid -- "$@" &
children+=("$!") # also the new process-group ID
}
cleanup() {
status=$?
trap - EXIT HUP INT TERM
# Ask each child process group to terminate.
for pgid in "${children[@]}"; do
kill -TERM -- "-$pgid" 2>/dev/null || :
done
sleep 1
# Forcefully remove anything still alive.
for pgid in "${children[@]}"; do
kill -KILL -- "-$pgid" 2>/dev/null || :
done
wait 2>/dev/null || :
exit "$status"
}
trap cleanup EXIT
trap 'exit 129' HUP
trap 'exit 130' INT
trap 'exit 143' TERM
run command1 arg1
run command2 arg2
wait
```
The negative PID in `kill -- "-$pgid"` means “signal the entire process group,” so grandchildren are included unless they deliberately create another session/process group.
Important limitations:
- Traps do not run for `SIGKILL`, kernel crashes, or abrupt power loss.
- `pkill -P $$` and `jobs -p` only reliably find direct children and are race-prone.
- Avoid `kill 0` unless the script is guaranteed to have its own process group; otherwise it may kill the invoking shell too.
For hard guarantees, run the script under a supervisor using a cgroup—e.g. systemd with its default `KillMode=control-group`. On Linux, another option is making each child set `prctl(PR_SET_PDEATHSIG, ...)`, though Bash cannot do that natively.
End of response · 56 lines · 1,625 characters · sha256 298f340626ef08d261fa058b236f8f794352e269a705f17191d33aa7681c12fe