Gemini on Question 14, Sep 27
I'm writing a bash script to purge orphaned children after the parent dies. What's the idiomatic way?The most idiomatic way to handle this in Bash is to use a trap on the EXIT pseudo-signal.
When you set a trap on EXIT, Bash will execute your cleanup code regardless of whether the script finishes normally, errors out, or is terminated by a standard signal (like SIGINT from Ctrl+C or SIGTERM).
Here are the three best approaches, depending on how complex your child processes are.
1. The Standard Way (Direct Children)
If your script simply spawns a few background processes (&) and you want them to die when the script dies, use jobs -p inside an EXIT trap.
#!/bin/bash
# Define the cleanup function
cleanup() {
# Get PIDs of all background jobs started by this script
local pids=$(jobs -p)
if [[ -n "$pids" ]]; then
echo "Cleaning up orphaned children..."
# 2>/dev/null hides errors if a child already died naturally
kill $pids 2>/dev/null
fi
}
# Register the cleanup function to run on EXIT
trap cleanup EXIT
# --- Your Script Here ---
sleep 100 & # Child 1
sleep 200 & # Child 2
echo "Parent doing work. Press Ctrl+C to test."
sleep 10 # Parent work2. The Process Group Way (Deep Process Trees)
The jobs -p method only kills direct children. If your child processes spawn their own children, those grandchildren will still be orphaned.
To kill the entire process tree, you must send a signal to the whole Process Group. You do this by negating the parent's PID (-$$).
Note: You must reset the trap inside the trap function to prevent a recursive loop, because sending a signal to the process group also sends it to the parent script itself.
#!/bin/bash
# Run this script in its own process group to avoid killing the caller's terminal
set -m
cleanup() {
echo "Killing entire process group..."
# Disable the trap so we don't infinitely loop when we kill ourselves
trap - EXIT
# Send SIGTERM to the process group
kill -- -$$ 2>/dev/null
}
trap cleanup EXIT INT TERM
# --- Your Script Here ---
# Even if this child spawns more children, they all die
bash -c 'sleep 100 & sleep 100' &
echo "Parent doing work..."
sleep 103. The Flawless Linux Way (SIGKILL protection)
The big caveat with Bash traps: If your parent script is killed with kill -9 (SIGKILL), the trap will not fire, and your children will be orphaned.
If you are on Linux and need a 100% guarantee that children die even if the parent receives kill -9, Bash alone cannot do it. You have to tell the Linux kernel to issue a pdeathsig (Parent Death Signal).
You can achieve this idiomatically by wrapping the child execution in a tiny Python or Perl one-liner, or by using prlimit. Here is the easiest way using standard Linux tools (util-linux):
#!/bin/bash
# No trap needed for this specific child!
# `tail --pid=$$ -f /dev/null` waits for the parent ($$) to die.
# When the parent dies, tail exits, closing the pipe.
# The child process will receive a SIGPIPE or EOF and die.
(
# This subshell runs the child command
exec my_child_command
) < <(tail --pid=$$ -f /dev/null)
# Alternatively, if you have systemd installed, you can use systemd-run:
# systemd-run --user --scope -p "Delegate=yes" ./your_script.shSummary Recommendation
- •Use Method 1 (
trap+jobs -p) for 95% of standard Bash scripts. It is the most readable and standard way to do things in Bash. - •Use Method 2 (
kill -- -$$) if your children are spawning grandchildren. - •Worry about Method 3 only if you are building a robust daemon where
kill -9leaving orphans would cause a critical system failure.
Exact recordShowHideThe model id, the run, the verbatim text with line numbers and its full sha256
- Scored
- answered
- Received
- 2026-09-27 09:06:58 UTC
- Run
- 20260927T090007Z-7adb14
- Served by
- Google · HTTP 200 · finish stop
- Size
- 3,684 characters · 2,506 tokens out incl. hidden reasoning · 21.7 s
End of response · 93 lines · 3,684 characters · sha256 24d4e2a6109e05724c75e8c230b0d8ef4b65b12ef112f89e09f4b92b819956f4