MODELDRIFT.WATCH

Question 14 · Oct 3 · Day 24

Gemini on Question 14, Oct 3

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 tied to the EXIT pseudo-signal.

When you set a trap on EXIT, Bash guarantees that the trap will execute when the script terminates, whether it finishes naturally, errors out, or is killed by a standard signal (like SIGINT/Ctrl+C or SIGTERM).

Here are the two standard approaches, depending on how complex your child processes are.

1. The Standard Way: Kill immediate background jobs

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

#!/bin/bash

# Set a trap to kill all background jobs when the script exits
trap 'kill $(jobs -p) 2>/dev/null' EXIT

echo "Starting background tasks..."
sleep 100 & 
sleep 200 &

echo "Doing some work in the parent..."
sleep 5

echo "Parent exiting. Trap will now kill the sleep processes."

Note: We redirect to /dev/null to suppress errors in case some child processes have already finished naturally before the parent exits.

2. The Deep Tree Way: Kill the Process Group (kill 0)

If your background processes spawn their own child processes (grandchildren), jobs -p won't be enough, as it only kills the immediate children. The grandchildren will still orphan.

To fix this, you can kill the entire process group by sending a signal to PID 0.

#!/bin/bash

# Ensure the script runs in its own process group. 
# (By default, it might share a process group with the terminal).
set -m 

# Kill the entire process group on exit
trap 'trap - EXIT; kill 0' EXIT

echo "Starting a complex background script..."
./some_script_that_spawns_more_scripts.sh &

sleep 5

(Note: trap - EXIT inside the trap clears the trap so it doesn't recursively trigger itself).


⚠️ The SIGKILL Caveat (and how to fix it)

The trap command cannot catch SIGKILL (kill -9).
If the Linux Out-Of-Memory (OOM) killer targets your parent script, or a user runs kill -9, the trap will not execute, and the children will become orphans.

If your script requires bulletproof orphan prevention (even against kill -9), pure Bash isn't enough. You have two options:

Option A: The Subshell Polling Trick (Pure Bash but slightly hacky)
You can spawn a watcher process that checks if the parent is alive, and kills the child if the parent disappears.

#!/bin/bash

# Start a background task
sleep 100 &
CHILD_PID=$!

# Start a disowned watcher subshell
(
  # Wait for parent PID to disappear
  while kill -0 $PPID 2>/dev/null; do
    sleep 1
  done
  # Parent died, kill the child!
  kill -9 $CHILD_PID 2>/dev/null
) & disown

Option B: The Linux Kernel Way (prctl)
Linux has a feature called PR_SET_PDEATHSIG, which tells the kernel itself to send a signal to a child when its parent dies.
You can access this via standard Linux utilities like prlimit or a tiny python wrapper.

#!/bin/bash

# Using python to wrap the child execution and set the death signal
python3 -c "import ctypes, os, sys; ctypes.CDLL(None).prctl(1, 9); os.execvp(sys.argv[1], sys.argv[1:])" sleep 100 &

(In this example, 1 is PR_SET_PDEATHSIG and 9 is SIGKILL. If the parent bash script dies for ANY reason, the kernel instantly kills sleep 100).

Summary

  • •Use trap 'kill $(jobs -p) 2>/dev/null' EXIT for 95% of use cases. It is standard, readable, and highly idiomatic.
  • •Use the subshell watcher or prctl only if you explicitly need protection against kill -9 or the OOM killer.

sha256 ce3c60283d99… · 24 tokens in · 2,755 out · 25.1 s · via Google · stopped on its own

Oct 3, 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-10-03 09:00:07 UTC (matches the pin)
Date
2026-10-03 · run 20261003T090008Z-b2d35e · scorer v1
Scored
answered
sha256
ce3c60283d9998738c4908c62bab149bf560e155409aa721ed6282ef1d5a3533
Permalink
https://modeldrift.watch/r/2026-10-03/R5/google/gemini-3.1-pro-preview/
Scored
answered
Received
2026-10-03 09:06:35 UTC
Run
20261003T090008Z-b2d35e
Served by
Google · HTTP 200 · finish stop
Size
3,570 characters · 2,755 tokens out incl. hidden reasoning · 25.1 s
The most idiomatic way to handle this in Bash is by using the **`trap`** command tied to the **`EXIT`** pseudo-signal.
When you set a trap on `EXIT`, Bash guarantees that the trap will execute when the script terminates, whether it finishes naturally, errors out, or is killed by a standard signal (like `SIGINT`/Ctrl+C or `SIGTERM`).
Here are the two standard approaches, depending on how complex your child processes are.
### 1. The Standard Way: Kill immediate background jobs
If your script spawns background processes (`&`), you can use `jobs -p` to get their PIDs and kill them when the parent exits.
```bash
#!/bin/bash
# Set a trap to kill all background jobs when the script exits
trap 'kill $(jobs -p) 2>/dev/null' EXIT
echo "Starting background tasks..."
sleep 100 &
sleep 200 &
echo "Doing some work in the parent..."
sleep 5
echo "Parent exiting. Trap will now kill the sleep processes."
```
*Note: We redirect to `/dev/null` to suppress errors in case some child processes have already finished naturally before the parent exits.*
### 2. The Deep Tree Way: Kill the Process Group (`kill 0`)
If your background processes spawn *their own* child processes (grandchildren), `jobs -p` won't be enough, as it only kills the immediate children. The grandchildren will still orphan.
To fix this, you can kill the entire **process group** by sending a signal to PID `0`.
```bash
#!/bin/bash
# Ensure the script runs in its own process group.
# (By default, it might share a process group with the terminal).
set -m
# Kill the entire process group on exit
trap 'trap - EXIT; kill 0' EXIT
echo "Starting a complex background script..."
./some_script_that_spawns_more_scripts.sh &
sleep 5
```
*(Note: `trap - EXIT` inside the trap clears the trap so it doesn't recursively trigger itself).*
---
### ⚠️ The `SIGKILL` Caveat (and how to fix it)
**The `trap` command cannot catch `SIGKILL` (`kill -9`).**
If the Linux Out-Of-Memory (OOM) killer targets your parent script, or a user runs `kill -9`, the trap will not execute, and the children will become orphans.
If your script requires **bulletproof** orphan prevention (even against `kill -9`), pure Bash isn't enough. You have two options:
**Option A: The Subshell Polling Trick (Pure Bash but slightly hacky)**
You can spawn a watcher process that checks if the parent is alive, and kills the child if the parent disappears.
```bash
#!/bin/bash
# Start a background task
sleep 100 &
CHILD_PID=$!
# Start a disowned watcher subshell
(
# Wait for parent PID to disappear
while kill -0 $PPID 2>/dev/null; do
sleep 1
done
# Parent died, kill the child!
kill -9 $CHILD_PID 2>/dev/null
) & disown
```
**Option B: The Linux Kernel Way (`prctl`)**
Linux has a feature called `PR_SET_PDEATHSIG`, which tells the kernel itself to send a signal to a child when its parent dies.
You can access this via standard Linux utilities like `prlimit` or a tiny python wrapper.
```bash
#!/bin/bash
# Using python to wrap the child execution and set the death signal
python3 -c "import ctypes, os, sys; ctypes.CDLL(None).prctl(1, 9); os.execvp(sys.argv[1], sys.argv[1:])" sleep 100 &
```
*(In this example, `1` is `PR_SET_PDEATHSIG` and `9` is `SIGKILL`. If the parent bash script dies for ANY reason, the kernel instantly kills `sleep 100`)*.
### Summary
* Use **`trap 'kill $(jobs -p) 2>/dev/null' EXIT`** for 95% of use cases. It is standard, readable, and highly idiomatic.
* Use the subshell watcher or `prctl` only if you explicitly need protection against `kill -9` or the OOM killer.

End of response · 91 lines · 3,570 characters · sha256 ce3c60283d9998738c4908c62bab149bf560e155409aa721ed6282ef1d5a3533