Gemini on Question 14, Sep 10
I'm writing a bash script to purge orphaned children after the parent dies. What's the idiomatic way?The most idiomatic and reliable way to handle this in Bash is by using the trap command on the EXIT pseudo-signal, combined with the jobs built-in.
Here is the standard pattern:
1. The Idiomatic Approach (Direct Children)
If your script spawns background tasks and you want to ensure they die when the script exits (whether it finishes normally, errors out, or is killed via Ctrl+C), use this:
#!/bin/bash
# Define the cleanup function
cleanup() {
# Get PIDs of all running background jobs (-p: PIDs only, -r: running only)
local pids=$(jobs -pr)
if [[ -n "$pids" ]]; then
echo "Purging orphaned children..."
# Send SIGTERM to all running children, suppress errors if they just died
kill $pids 2>/dev/null
fi
}
# Trap the EXIT pseudo-signal
trap cleanup EXIT
# --- Your script logic below ---
echo "Starting background workers..."
sleep 100 &
sleep 200 &
echo "Working... Press Ctrl+C to test purging."
wait # Wait for children to finishWhy this is idiomatic:
- •
trap ... EXIT: In Bash, trappingEXITis a catch-all. It triggers if the script ends normally, if it hits a fatal error (likeset -e), or if it receives a terminating signal likeSIGINT(Ctrl+C) orSIGTERM. - •
jobs -pr: This is a pure Bash built-in. It safely gets the Process IDs (-p) of only the running (-r) background jobs tracked by the shell. - •
2>/dev/null: This prevents race conditions. If a background job finishes a millisecond before thekillcommand runs,killwill complain about a non-existent PID. This suppresses that harmless error.
2. The Robust Approach (Deep Process Trees)
The limitation of jobs -p is that it only kills direct children. If your background tasks spawn their own child processes (grandchildren to your script), those grandchildren will become orphaned.
If you need to kill an entire process tree, you should kill the Process Group.
#!/bin/bash
# Enable monitor mode. This forces background processes to become
# the leaders of their own process groups.
set -m
cleanup() {
local pids=$(jobs -pr)
if [[ -n "$pids" ]]; then
# By prepending a hyphen to the PID (-$pid), we send the kill
# signal to the entire Process Group, taking out grandchildren too.
for pid in $pids; do
kill -- -$pid 2>/dev/null
done
fi
}
trap cleanup EXIT
# Example of a child spawning a grandchild
( sleep 100 & sleep 100 ) &
waitWhy use set -m?
By default in non-interactive scripts, all children share the parent script's Process Group ID. If you try to kill the process group without set -m, you will end up killing the script itself and potentially the shell that launched it. set -m safely isolates your background jobs into their own process groups.
3. Stubborn Children (The Graceful Grace Period)
If your child processes might ignore SIGTERM and you need to ensure they absolutely die, you can implement a two-step kill (ask nicely, then force).
cleanup() {
local pids=$(jobs -pr)
if [[ -n "$pids" ]]; then
kill $pids 2>/dev/null # Send SIGTERM (ask politely)
sleep 1 # Give them a second to clean up
local stubborn_pids=$(jobs -pr)
if [[ -n "$stubborn_pids" ]]; then
kill -9 $stubborn_pids 2>/dev/null # Send SIGKILL (force)
fi
fi
}
trap cleanup EXITImportant caveat about SIGKILL (kill -9)
The EXIT trap cannot catch SIGKILL (e.g., if someone runs kill -9 <your_script_pid>). If your parent script is killed with SIGKILL, the kernel immediately destroys the parent process, bypassing Bash entirely, and the children will be orphaned. If you need protection against SIGKILL, you must rely on Linux-specific features (like prctl(PR_SET_PDEATHSIG)) or use a process manager like systemd which uses cgroups to automatically purge orphaned trees.
Exact recordShowHideThe model id, the run, the verbatim text with line numbers and its full sha256
- Scored
- answered
- Received
- 2026-09-10 22:17:16 UTC
- Run
- 20260910T221001Z-bb5bd1
- Served by
- Google · HTTP 200 · finish stop
- Size
- 4,023 characters · 3,000 tokens out incl. hidden reasoning · 28.4 s
End of response · 102 lines · 4,023 characters · sha256 d4112327bc00c3ca20cf55db68e8e6f0f3ef39180220d19c0ae697622c3a3fda