MODELDRIFT.WATCH

Question 14 · Sep 15 · Day 6

Gemini on Question 14, Sep 15

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 by using the trap command hooked to the EXIT pseudo-signal.

When you trap EXIT, Bash guarantees that the trap will run whether the script finishes normally, aborts due to set -e, or receives a standard termination signal (like SIGINT/Ctrl+C or SIGTERM).

Here are the two standard approaches, depending on how aggressive you need to be.

1. The Safe Way: Killing known background jobs

This method explicitly kills only the processes that Bash started in the background using &.

#!/bin/bash

# Define the cleanup function
cleanup() {
    echo "Parent dying. Cleaning up children..."
    # jobs -p lists the PIDs of all background jobs started by this script
    # 2>/dev/null hides errors if the children already exited naturally
    kill $(jobs -p) 2>/dev/null
}

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

# --- Your script logic below ---

echo "Starting child 1..."
sleep 100 &

echo "Starting child 2..."
sleep 200 &

# Wait for all background jobs to finish normally
wait

2. The Aggressive Way: Killing the whole Process Group

If your background children spawn their own children (grandchildren), jobs -p won't catch them. To kill the script, its children, and its grandchildren, you send a signal to the entire process group using kill 0.

#!/bin/bash

# Send SIGTERM to the entire process group
trap 'trap - EXIT; kill 0' EXIT

sleep 100 &
bash -c 'sleep 200' & # A child that spawns a grandchild

wait

Note: trap - EXIT is included inside the trap to unregister it. This prevents an infinite loop where the script sends a kill signal to its group, receives it, triggers the trap again, etc.


⚠️ The Fatal Flaw of Bash (and how to fix it)

No bash trap can catch kill -9 (SIGKILL).
If your parent script is killed via SIGKILL or an Out-Of-Memory (OOM) event, the trap will not fire, and your children will become orphaned.

If you need a 100% bulletproof guarantee (on Linux) that children will die when the parent dies, pure bash cannot do it. You have to ask the Linux kernel to do it using prctl(PR_SET_PDEATHSIG).

Since Bash can't make system calls directly, you can wrap your child processes in a tiny Python or Perl one-liner when you launch them:

#!/bin/bash

# This child will die instantly if the parent bash script is kill -9'd
perl -MPOSIX -e 'require "syscall.ph"; syscall(&SYS_prctl, 1, 9); exec @ARGV' -- sleep 100 &

wait

(In the perl command above, 1 is PR_SET_PDEATHSIG and 9 is SIGKILL. When the parent dies, the kernel immediately sends SIGKILL to the child).

Summary: Stick to the trap cleanup EXIT with jobs -p for 99% of use cases. Only reach for the Perl/kernel trick if you have a strict requirement that no orphans can ever exist, even in the event of SIGKILL.

sha256 b7e1b87fa864… · 24 tokens in · 2,169 out · 19.7 s · via Google · stopped on its own

Sep 15, 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-15 09:00:03 UTC (matches the pin)
Date
2026-09-15 · run 20260915T090004Z-599f39 · scorer v1
Scored
answered
sha256
b7e1b87fa8649d338c42b8d75dd096ebd2ed3d3360325441b2c35929e877f6cf
Permalink
https://modeldrift.watch/r/2026-09-15/R5/google/gemini-3.1-pro-preview/
Scored
answered
Received
2026-09-15 09:07:23 UTC
Run
20260915T090004Z-599f39
Served by
Google · HTTP 200 · finish stop
Size
2,894 characters · 2,169 tokens out incl. hidden reasoning · 19.7 s
The most idiomatic way to handle this in Bash is by using the **`trap`** command hooked to the **`EXIT`** pseudo-signal.
When you trap `EXIT`, Bash guarantees that the trap will run whether the script finishes normally, aborts due to `set -e`, or receives a standard termination signal (like `SIGINT`/Ctrl+C or `SIGTERM`).
Here are the two standard approaches, depending on how aggressive you need to be.
### 1. The Safe Way: Killing known background jobs
This method explicitly kills only the processes that Bash started in the background using `&`.
```bash
#!/bin/bash
# Define the cleanup function
cleanup() {
echo "Parent dying. Cleaning up children..."
# jobs -p lists the PIDs of all background jobs started by this script
# 2>/dev/null hides errors if the children already exited naturally
kill $(jobs -p) 2>/dev/null
}
# Register the cleanup function to run on EXIT
trap cleanup EXIT
# --- Your script logic below ---
echo "Starting child 1..."
sleep 100 &
echo "Starting child 2..."
sleep 200 &
# Wait for all background jobs to finish normally
wait
```
### 2. The Aggressive Way: Killing the whole Process Group
If your background children spawn their *own* children (grandchildren), `jobs -p` won't catch them. To kill the script, its children, and its grandchildren, you send a signal to the entire process group using `kill 0`.
```bash
#!/bin/bash
# Send SIGTERM to the entire process group
trap 'trap - EXIT; kill 0' EXIT
sleep 100 &
bash -c 'sleep 200' & # A child that spawns a grandchild
wait
```
*Note: `trap - EXIT` is included inside the trap to unregister it. This prevents an infinite loop where the script sends a kill signal to its group, receives it, triggers the trap again, etc.*
---
### ⚠️ The Fatal Flaw of Bash (and how to fix it)
**No bash `trap` can catch `kill -9` (SIGKILL).**
If your parent script is killed via `SIGKILL` or an Out-Of-Memory (OOM) event, the trap will not fire, and your children **will** become orphaned.
If you need a 100% bulletproof guarantee (on Linux) that children will die when the parent dies, pure bash cannot do it. You have to ask the Linux kernel to do it using `prctl(PR_SET_PDEATHSIG)`.
Since Bash can't make system calls directly, you can wrap your child processes in a tiny Python or Perl one-liner when you launch them:
```bash
#!/bin/bash
# This child will die instantly if the parent bash script is kill -9'd
perl -MPOSIX -e 'require "syscall.ph"; syscall(&SYS_prctl, 1, 9); exec @ARGV' -- sleep 100 &
wait
```
*(In the perl command above, `1` is `PR_SET_PDEATHSIG` and `9` is `SIGKILL`. When the parent dies, the kernel immediately sends SIGKILL to the child).*
**Summary:** Stick to the `trap cleanup EXIT` with `jobs -p` for 99% of use cases. Only reach for the Perl/kernel trick if you have a strict requirement that no orphans can ever exist, even in the event of `SIGKILL`.

End of response · 72 lines · 2,894 characters · sha256 b7e1b87fa8649d338c42b8d75dd096ebd2ed3d3360325441b2c35929e877f6cf