MODELDRIFT.WATCH

Question 14 · Sep 17 · Day 8

Gemini on Question 14, Sep 17

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 builtin command to catch the EXIT pseudo-signal.

When you set a trap on EXIT, Bash will execute your cleanup code regardless of whether the script finishes naturally, errors out, or is terminated by a standard signal (like SIGINT from Ctrl+C, or SIGTERM).

Here are the three most common and idiomatic ways to do it, depending on your exact needs:

1. The Job Control Method (Best for general use)

If your script spawns background tasks using &, you can use jobs -p to get a list of all currently running background process IDs spawned by the shell, and pass them to kill.

#!/bin/bash

# Suppress error messages in case the jobs already finished naturally
trap 'kill $(jobs -p) 2>/dev/null' EXIT

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

echo "Doing work in the foreground..."
sleep 5
echo "Done. Exiting will now kill the background sleeps."

2. The Array Method (Best for targeting specific processes)

If you only want to kill certain background processes, or if you are doing complex things where jobs might not be reliable, track the Process IDs (PIDs) manually in an array.

#!/bin/bash

pids=()

# Set the trap to kill only the PIDs in the array
trap 'kill "${pids[@]}" 2>/dev/null' EXIT

echo "Starting specific jobs..."
sleep 100 &
pids+=($!) # $! is the PID of the last background command

sleep 200 &
pids+=($!)

echo "Doing work..."
sleep 5

3. The Process Group Method (Best for deep process trees)

If your script spawns a child, and that child spawns grandchildren, Methods 1 and 2 will leave the grandchildren orphaned. To kill the entire process tree, you send a signal to the whole Process Group by using kill 0.

#!/bin/bash

# kill 0 sends a SIGTERM to all processes in the current process group
trap 'trap - EXIT; kill 0' EXIT

echo "Starting deep process tree..."
# Even if this script spawns other scripts, kill 0 gets them all
bash -c 'sleep 100' & 

sleep 5

Note: trap - EXIT unregisters the trap before triggering kill 0 to prevent infinite recursion/weird behavior, since kill 0 also sends the signal to the parent script itself.

⚠️ The One Crucial Caveat: kill -9

No script can catch SIGKILL (kill -9).
If someone runs kill -9 <your_script_pid>, the Linux kernel instantly terminates your Bash script. The trap will not fire, and your child processes will be orphaned.

If your application absolutely cannot tolerate orphans even in a kill -9 scenario, you must use Linux-specific system calls (like prctl(PR_SET_PDEATHSIG, SIGTERM)) which cannot be done natively in pure Bash. You would need to wrap your child commands in a utility like prlimit or a tiny Python/C wrapper. For 99% of Bash scripts, however, the trap ... EXIT method is the accepted standard.

sha256 6b12ec32f5fe… · 24 tokens in · 2,228 out · 20.9 s · via Google · stopped on its own

Sep 17, 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-17 09:00:21 UTC (matches the pin)
Date
2026-09-17 · run 20260917T090022Z-136fc3 · scorer v1
Scored
answered
sha256
6b12ec32f5fec104900408151bf2181664cc5dc13e1aa7785e0b6b7966aa2915
Permalink
https://modeldrift.watch/r/2026-09-17/R5/google/gemini-3.1-pro-preview/
Scored
answered
Received
2026-09-17 09:07:43 UTC
Run
20260917T090022Z-136fc3
Served by
Google · HTTP 200 · finish stop
Size
2,890 characters · 2,228 tokens out incl. hidden reasoning · 20.9 s
The idiomatic way to handle this in Bash is to use the **`trap`** builtin command to catch the `EXIT` pseudo-signal.
When you set a trap on `EXIT`, Bash will execute your cleanup code regardless of whether the script finishes naturally, errors out, or is terminated by a standard signal (like `SIGINT` from Ctrl+C, or `SIGTERM`).
Here are the three most common and idiomatic ways to do it, depending on your exact needs:
### 1. The Job Control Method (Best for general use)
If your script spawns background tasks using `&`, you can use `jobs -p` to get a list of all currently running background process IDs spawned by the shell, and pass them to `kill`.
```bash
#!/bin/bash
# Suppress error messages in case the jobs already finished naturally
trap 'kill $(jobs -p) 2>/dev/null' EXIT
echo "Starting background jobs..."
sleep 100 &
sleep 200 &
echo "Doing work in the foreground..."
sleep 5
echo "Done. Exiting will now kill the background sleeps."
```
### 2. The Array Method (Best for targeting specific processes)
If you only want to kill *certain* background processes, or if you are doing complex things where `jobs` might not be reliable, track the Process IDs (PIDs) manually in an array.
```bash
#!/bin/bash
pids=()
# Set the trap to kill only the PIDs in the array
trap 'kill "${pids[@]}" 2>/dev/null' EXIT
echo "Starting specific jobs..."
sleep 100 &
pids+=($!) # $! is the PID of the last background command
sleep 200 &
pids+=($!)
echo "Doing work..."
sleep 5
```
### 3. The Process Group Method (Best for deep process trees)
If your script spawns a child, and that child spawns *grandchildren*, Methods 1 and 2 will leave the grandchildren orphaned. To kill the entire process tree, you send a signal to the whole **Process Group** by using `kill 0`.
```bash
#!/bin/bash
# kill 0 sends a SIGTERM to all processes in the current process group
trap 'trap - EXIT; kill 0' EXIT
echo "Starting deep process tree..."
# Even if this script spawns other scripts, kill 0 gets them all
bash -c 'sleep 100' &
sleep 5
```
*Note: `trap - EXIT` unregisters the trap before triggering `kill 0` to prevent infinite recursion/weird behavior, since `kill 0` also sends the signal to the parent script itself.*
### ⚠️ The One Crucial Caveat: `kill -9`
**No script can catch `SIGKILL` (kill -9).**
If someone runs `kill -9 <your_script_pid>`, the Linux kernel instantly terminates your Bash script. The `trap` will **not** fire, and your child processes **will** be orphaned.
If your application absolutely cannot tolerate orphans even in a `kill -9` scenario, you must use Linux-specific system calls (like `prctl(PR_SET_PDEATHSIG, SIGTERM)`) which cannot be done natively in pure Bash. You would need to wrap your child commands in a utility like `prlimit` or a tiny Python/C wrapper. For 99% of Bash scripts, however, the `trap ... EXIT` method is the accepted standard.

End of response · 68 lines · 2,890 characters · sha256 6b12ec32f5fec104900408151bf2181664cc5dc13e1aa7785e0b6b7966aa2915