MODELDRIFT.WATCH

Question 14 · Sep 25 · Day 16

Gemini on Question 14, Sep 25

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 combined with the EXIT pseudo-signal.

Using EXIT is crucial because it fires regardless of how the script dies—whether it reaches the end normally, encounters a fatal error (set -e), or receives a termination signal (like SIGINT/Ctrl+C or SIGTERM).

Here are the best approaches depending on your specific needs:

1. The Standard Way: Killing Direct Children

If your script spawns background processes directly (using &), you can use jobs -p to get their PIDs and kill them when the script exits.

#!/bin/bash

# Define the cleanup function
cleanup() {
    # Get all background PIDs spawned by this shell
    local pids=$(jobs -p)
    if [[ -n "$pids" ]]; then
        echo "Purging orphaned children..."
        # Send SIGTERM to all children; suppress errors if they already died
        kill $pids 2>/dev/null 
    fi
}

# Trap the EXIT pseudo-signal
trap cleanup EXIT

# --- Your script logic here ---
sleep 100 &
sleep 200 &

echo "Doing work. Press Ctrl+C to test..."
wait

2. The Deep Way: Killing Children and Grandchildren

The limitation of jobs -p (and pkill -P $$) is that they only kill direct children. If your background process spawns its own children (grandchildren of your script), they will be orphaned.

To kill the entire process tree, you can send a signal to the script's Process Group by using kill 0.

#!/bin/bash

cleanup() {
    echo "Purging entire process group..."
    # 'trap - EXIT' prevents an infinite loop if kill 0 triggers the trap again
    trap - EXIT 
    
    # Send SIGTERM to all processes in the current process group
    kill 0
}

trap cleanup EXIT

# --- Your script logic here ---
# Even if this script spawns children that spawn children, 
# 'kill 0' will wipe them all out.
./some_deep_script.sh &

wait

Note: Be careful with kill 0 if you are source-ing this script in an interactive terminal, as it will kill your terminal session. It is completely safe for standalone executable scripts.

3. The pkill Alternative

If you prefer relying on process IDs rather than Bash's job control, you can use pkill targeting the Parent PID ($$ is the PID of the current script).

#!/bin/bash

trap 'pkill -P $$' EXIT

# ... spawn processes ...

Pro-Tips for Production Scripts

  • •Graceful vs. Forceful: kill sends SIGTERM by default. If your children might hang and ignore SIGTERM, you can escalate to SIGKILL (kill -9):
    ```bash
    cleanup() {
    local pids=$(jobs -p)
    if [[ -n "$pids" ]]; then
    kill $pids 2>/dev/null # Ask nicely
    sleep 1
    kill -9 $pids 2>/dev/null # Force kill if they didn't listen
    fi
    }
    ```
  • •Systemd: If you are writing this script to run as a long-running service/daemon on Linux, don't do this in bash. Let systemd handle it. Systemd puts the service in a Control Group (cgroup) and natively kills all children, grandchildren, and rogue processes when the parent service stops, with zero bash scripting required (KillMode=control-group is the default).

sha256 fa99a8755e8c… · 24 tokens in · 2,530 out · 21.3 s · via Google · stopped on its own

Sep 25, 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-25 09:00:06 UTC (matches the pin)
Date
2026-09-25 · run 20260925T090007Z-f08d1f · scorer v1
Scored
answered
sha256
fa99a8755e8c8c0aa6e3dad2a35cc96dd899778b8c97393070ef988367e27e69
Permalink
https://modeldrift.watch/r/2026-09-25/R5/google/gemini-3.1-pro-preview/
Scored
answered
Received
2026-09-25 09:07:20 UTC
Run
20260925T090007Z-f08d1f
Served by
Google · HTTP 200 · finish stop
Size
3,210 characters · 2,530 tokens out incl. hidden reasoning · 21.3 s
The most idiomatic and reliable way to handle this in Bash is by using the **`trap`** command combined with the **`EXIT`** pseudo-signal.
Using `EXIT` is crucial because it fires regardless of *how* the script dies—whether it reaches the end normally, encounters a fatal error (`set -e`), or receives a termination signal (like `SIGINT`/Ctrl+C or `SIGTERM`).
Here are the best approaches depending on your specific needs:
### 1. The Standard Way: Killing Direct Children
If your script spawns background processes directly (using `&`), you can use `jobs -p` to get their PIDs and kill them when the script exits.
```bash
#!/bin/bash
# Define the cleanup function
cleanup() {
# Get all background PIDs spawned by this shell
local pids=$(jobs -p)
if [[ -n "$pids" ]]; then
echo "Purging orphaned children..."
# Send SIGTERM to all children; suppress errors if they already died
kill $pids 2>/dev/null
fi
}
# Trap the EXIT pseudo-signal
trap cleanup EXIT
# --- Your script logic here ---
sleep 100 &
sleep 200 &
echo "Doing work. Press Ctrl+C to test..."
wait
```
### 2. The Deep Way: Killing Children and Grandchildren
The limitation of `jobs -p` (and `pkill -P $$`) is that they only kill **direct** children. If your background process spawns its own children (grandchildren of your script), they will be orphaned.
To kill the entire process tree, you can send a signal to the script's **Process Group** by using `kill 0`.
```bash
#!/bin/bash
cleanup() {
echo "Purging entire process group..."
# 'trap - EXIT' prevents an infinite loop if kill 0 triggers the trap again
trap - EXIT
# Send SIGTERM to all processes in the current process group
kill 0
}
trap cleanup EXIT
# --- Your script logic here ---
# Even if this script spawns children that spawn children,
# 'kill 0' will wipe them all out.
./some_deep_script.sh &
wait
```
*Note: Be careful with `kill 0` if you are `source`-ing this script in an interactive terminal, as it will kill your terminal session. It is completely safe for standalone executable scripts.*
### 3. The `pkill` Alternative
If you prefer relying on process IDs rather than Bash's job control, you can use `pkill` targeting the Parent PID (`$$` is the PID of the current script).
```bash
#!/bin/bash
trap 'pkill -P $$' EXIT
# ... spawn processes ...
```
### Pro-Tips for Production Scripts
* **Graceful vs. Forceful:** `kill` sends `SIGTERM` by default. If your children might hang and ignore `SIGTERM`, you can escalate to `SIGKILL` (kill -9):
```bash
cleanup() {
local pids=$(jobs -p)
if [[ -n "$pids" ]]; then
kill $pids 2>/dev/null # Ask nicely
sleep 1
kill -9 $pids 2>/dev/null # Force kill if they didn't listen
fi
}
```
* **Systemd:** If you are writing this script to run as a long-running service/daemon on Linux, **don't do this in bash**. Let `systemd` handle it. Systemd puts the service in a Control Group (cgroup) and natively kills all children, grandchildren, and rogue processes when the parent service stops, with zero bash scripting required (`KillMode=control-group` is the default).

End of response · 87 lines · 3,210 characters · sha256 fa99a8755e8c8c0aa6e3dad2a35cc96dd899778b8c97393070ef988367e27e69