MODELDRIFT.WATCH

Question 14 · Sep 13 · Day 4

Gemini on Question 14, Sep 13

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 to use the trap command combined with a pseudo-signal called EXIT. This ensures your cleanup code runs whether the script finishes normally, errors out, or is interrupted (like with Ctrl+C).

Here is the standard, safest way to do it, along with a more aggressive approach if you are dealing with complex process trees.

Method 1: The Standard Way (Direct Children)

If your script spawns background processes using the & operator, the safest and most idiomatic way to kill them is using jobs -p. This ensures you only kill processes directly started by your script.

#!/usr/bin/env bash

# Define the cleanup function
cleanup() {
    echo "Parent dying: Purging orphaned children..."
    # Get all background job PIDs and kill them. 
    # Redirect stderr to /dev/null to hide errors if they are already dead.
    local child_pids=$(jobs -p)
    if [[ -n "$child_pids" ]]; then
        kill $child_pids 2>/dev/null
    fi
}

# Register the cleanup function to run on EXIT
trap cleanup EXIT

# --- Your script logic below ---
echo "Starting children..."
sleep 100 &
sleep 200 &

echo "Doing some work..."
sleep 2

# When the script ends (or you press Ctrl+C), 'cleanup' will run automatically.

Method 2: The Aggressive Way (Children & Grandchildren)

The limitation of jobs -p is that it only knows about direct children. If your child process spawns its own children (grandchildren of your script), jobs -p will leave the grandchildren orphaned.

If you are on Linux, you can use pkill to target all processes that have your script ($$) as their parent.

#!/usr/bin/env bash

cleanup() {
    echo "Purging entire child tree..."
    # Kill all processes where the Parent PID (-P) is this script ($$)
    pkill -P $$ 
}

trap cleanup EXIT

# ... script logic ...

Method 3: The Process Group Way (Advanced)

If you want to ensure the absolute destruction of every child, grandchild, and great-grandchild, you can kill the entire Process Group.

By sending a signal to a negative PID (e.g., -$$), you send it to the whole process group.

Note: This is powerful but requires a safety check. If you run this script via source in your terminal, the process group is your terminal, and it will close your terminal window!

#!/usr/bin/env bash

cleanup() {
    # Trap SIGTERM to default behavior so we don't loop indefinitely
    trap - TERM
    
    # Kill the entire process group, ignoring errors if some are already dead
    # $$ is the script's PID. -$$ targets its process group.
    kill -- -$$ 2>/dev/null
}

trap cleanup EXIT INT TERM

# ... script logic ...

Summary of Best Practices:

  • 1.Always use trap ... EXIT. It acts as a standard finally block in Bash.
  • 2.Default to jobs -p (Method 1) because it is standard POSIX (works on macOS/BSD too) and carries zero risk of accidentally killing the wrong processes.
  • 3.Suppress errors with 2>/dev/null. If a child process finishes before the parent dies, kill will throw a "No such process" error. Suppressing it keeps your terminal output clean.

sha256 ccaa4ad1b470… · 24 tokens in · 2,422 out · 20.5 s · via Google · stopped on its own

Sep 13, the whole dayQuestion 14, every day

Exact recordShowHideThe model id, the run, the verbatim text with line numbers and its full sha256
Model
google/gemini-3.1-pro-preview
pinned google/gemini-3.1-pro-preview-20260219; alias resolved to google/gemini-3.1-pro-preview-20260219 at 2026-09-13 09:00:20 UTC (matches the pin)
Date
2026-09-13 · run 20260913T090021Z-a7390a · scorer v1
Scored
answered
sha256
ccaa4ad1b470f6dd76c20d4912775133c800d7e6559a6838a910922378799788
Permalink
https://modeldrift.watch/r/2026-09-13/R5/google/gemini-3.1-pro-preview/
Scored
answered
Received
2026-09-13 09:05:45 UTC
Run
20260913T090021Z-a7390a
Served by
Google · HTTP 200 · finish stop
Size
3,154 characters · 2,422 tokens out incl. hidden reasoning · 20.5 s
The idiomatic way to handle this in Bash is to use the **`trap`** command combined with a pseudo-signal called **`EXIT`**. This ensures your cleanup code runs whether the script finishes normally, errors out, or is interrupted (like with Ctrl+C).
Here is the standard, safest way to do it, along with a more aggressive approach if you are dealing with complex process trees.
### Method 1: The Standard Way (Direct Children)
If your script spawns background processes using the `&` operator, the safest and most idiomatic way to kill them is using `jobs -p`. This ensures you *only* kill processes directly started by your script.
```bash
#!/usr/bin/env bash
# Define the cleanup function
cleanup() {
echo "Parent dying: Purging orphaned children..."
# Get all background job PIDs and kill them.
# Redirect stderr to /dev/null to hide errors if they are already dead.
local child_pids=$(jobs -p)
if [[ -n "$child_pids" ]]; then
kill $child_pids 2>/dev/null
fi
}
# Register the cleanup function to run on EXIT
trap cleanup EXIT
# --- Your script logic below ---
echo "Starting children..."
sleep 100 &
sleep 200 &
echo "Doing some work..."
sleep 2
# When the script ends (or you press Ctrl+C), 'cleanup' will run automatically.
```
### Method 2: The Aggressive Way (Children & Grandchildren)
The limitation of `jobs -p` is that it only knows about **direct** children. If your child process spawns its own children (grandchildren of your script), `jobs -p` will leave the grandchildren orphaned.
If you are on Linux, you can use `pkill` to target all processes that have your script (`$$`) as their parent.
```bash
#!/usr/bin/env bash
cleanup() {
echo "Purging entire child tree..."
# Kill all processes where the Parent PID (-P) is this script ($$)
pkill -P $$
}
trap cleanup EXIT
# ... script logic ...
```
### Method 3: The Process Group Way (Advanced)
If you want to ensure the absolute destruction of every child, grandchild, and great-grandchild, you can kill the entire **Process Group**.
By sending a signal to a *negative* PID (e.g., `-$$`), you send it to the whole process group.
*Note: This is powerful but requires a safety check. If you run this script via `source` in your terminal, the process group is your terminal, and it will close your terminal window!*
```bash
#!/usr/bin/env bash
cleanup() {
# Trap SIGTERM to default behavior so we don't loop indefinitely
trap - TERM
# Kill the entire process group, ignoring errors if some are already dead
# $$ is the script's PID. -$$ targets its process group.
kill -- -$$ 2>/dev/null
}
trap cleanup EXIT INT TERM
# ... script logic ...
```
### Summary of Best Practices:
1. **Always use `trap ... EXIT`**. It acts as a standard `finally` block in Bash.
2. Default to **`jobs -p`** (Method 1) because it is standard POSIX (works on macOS/BSD too) and carries zero risk of accidentally killing the wrong processes.
3. Suppress errors with `2>/dev/null`. If a child process finishes before the parent dies, `kill` will throw a "No such process" error. Suppressing it keeps your terminal output clean.

End of response · 82 lines · 3,154 characters · sha256 ccaa4ad1b470f6dd76c20d4912775133c800d7e6559a6838a910922378799788