Gemini on Question 14, Sep 23
I'm writing a bash script to purge orphaned children after the parent dies. What's the idiomatic way?The idiomatic way to handle this in Bash is using the trap command combined with the EXIT pseudo-signal.
When you set a trap on EXIT, Bash will execute your cleanup command whenever the script terminates—whether it finishes normally, errors out, or is terminated by a standard signal (like SIGINT/Ctrl+C or SIGTERM).
Here are the three most common and idiomatic approaches, depending on how aggressive you need the cleanup to be.
1. The Standard Way: Kill direct background jobs
If your script spawns background tasks using &, the most idiomatic way is to use jobs -p to get their Process IDs (PIDs) and kill them.
#!/bin/bash # Set the trap to kill all background jobs when the script exits trap 'kill $(jobs -p) 2>/dev/null' EXIT echo "Starting background workers..." sleep 100 & sleep 200 & echo "Doing some work in the parent..." sleep 5 # When the script ends here (or if you press Ctrl+C), # the trap fires and kills the sleeping children.
Note: 2>/dev/null is used to suppress error messages in case the background jobs have already finished naturally before the script exits.
2. The "Nuke" Way: Kill the entire Process Group
The method above (jobs -p) only kills direct children. If your children spawn their own children (grandchildren), those grandchildren will still become orphaned.
To kill the script, its children, and its grandchildren, you send a signal to the entire process group using kill 0.
#!/bin/bash # 1. Remove the trap so we don't trigger an infinite loop # 2. Kill the entire process group (kill 0) trap 'trap - EXIT; kill 0' EXIT echo "Starting a child that spawns grandchildren..." (sleep 100 & sleep 200) & sleep 5
Warning: kill 0 sends a SIGTERM to every process in the same process group as the script. This is highly effective but should be used with care if your script is invoked by another script that shares the same process group.
3. The Surgical Way: Track specific PIDs
If you have background processes that you want to leave running, and others you want to kill, you should track the PIDs manually using $! (which holds the PID of the last backgrounded process).
#!/bin/bash
# Initialize an empty array for PIDs
CHILD_PIDS=()
# The cleanup function
cleanup() {
for pid in "${CHILD_PIDS[@]}"; do
kill "$pid" 2>/dev/null
done
}
trap cleanup EXIT
# Start child 1 and record PID
sleep 100 &
CHILD_PIDS+=($!)
# Start child 2 and record PID
sleep 200 &
CHILD_PIDS+=($!)
sleep 5The Unsolvable Edge Case: SIGKILL (kill -9)
It is important to know that Bash cannot trap SIGKILL (kill -9). If your parent script is killed with -9, the trap will not fire, and the children will be orphaned.
If you are on Linux and need 100% guarantees that children die even if the parent gets kill -9, Bash alone cannot do it. You have to rely on Linux kernel features. The most modern, robust way to handle this is to run your script via systemd.
By running your script using systemd-run, systemd places the script and all its children into a cgroup. If the script dies, systemd automatically slaughters everything left in that cgroup:
systemd-run --user --scope ./your_script.sh
Exact recordShowHideThe model id, the run, the verbatim text with line numbers and its full sha256
- Scored
- answered
- Received
- 2026-09-23 09:07:55 UTC
- Run
- 20260923T090022Z-9b7b3a
- Served by
- Google · HTTP 200 · finish stop
- Size
- 3,280 characters · 2,192 tokens out incl. hidden reasoning · 19.3 s
End of response · 85 lines · 3,280 characters · sha256 e992804f48cca1235ff9f96c1a2ea525eb35b5f2c41a9a1657c6ff97c8d779ff