Skip to content
Development
Skill

/shell-process-patterns

Safely start, supervise, and terminate shell processes: background jobs, PID capture, signals, traps, cleanup verification.

From plugin
vexjoy-agent
421122 skills198 agents11 commands76 hooks
Install
$ npx -y skills add notque/vexjoy-agent --skill shell-process-patterns --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/shell-process-patterns

Context preview

The summary Claude sees to decide when to auto-load this skill.

Safely start, supervise, and terminate shell processes: background jobs, PID capture, signals, traps, cleanup verification.

SKILL.md

shell-process-patterns.SKILL.md
name: shell-process-patterns
description: "Safely start, supervise, and terminate shell processes: background jobs, PID capture, signals, traps, cleanup verification."
user-invocable: false
allowed-tools:
  - Read
  - Write
  - Bash
  - Grep
  - Glob
  - Edit
routing:
  triggers:
    - "background process"
    - "nohup"
    - "kill process"
    - "pid lookup"
    - "shell cleanup"
    - "trap handler"
    - "signal handling"
    - "set -e"
    - "bash process"
  pairs_with:
    - condition-based-waiting
    - service-health-check
    - cron-automation
  category: process

Shell Process Patterns

Start, supervise, and terminate shell processes safely -- background jobs, subshells, signal handlers, and cleanup. The dominant failure mode in this domain is silent state: a process that looks killed but still holds a port, a trap that looked fine but never fired on the child, a `set -e` script that kept running because `|| true` swallowed the error. This skill picks the right pattern, implements it with the real PID (not the wrapper), and verifies the observable state afterward.

| Pattern | Use When | Key Safety Bound | |---------|----------|------------------| | Background start | Ad-hoc long-running child in a script or session | Redirect fd 0/1/2, capture real PID, disown if parent exits | | Daemonization | Process must survive terminal close, become session leader | `setsid` + fd redirect + write PID file atomically | | PID resolution | Need to kill / inspect the actual worker | Re-query with `ss`/`pgrep`/`lsof`; `$!` is advisory, not authoritative | | Signal discipline | Graceful shutdown of a supervisor + children | SIGTERM first with timeout, SIGKILL as last resort, propagate to process group | | Trap + cleanup | Script must leave no orphans, lock files, or temp dirs | `trap ... EXIT` + verification (file gone, port free, PID dead) | | Strict-mode scripts | Any non-trivial bash script | `set -euo pipefail` with understood `||` and `if !` escape hatches |

Scope

**In scope:**

  • Starting background processes (`&`, `nohup`, `disown`, `setsid`, daemonization).
  • Capturing the actual child PID and reconciling it against observed system state.
  • Signal handling -- SIGTERM vs SIGKILL, `exec` semantics, process groups, subshell inheritance.
  • Trap discipline -- `EXIT` vs signal traps, ordering, inheritance in subshells and functions.
  • Cleanup verification -- kill-and-check, not kill-and-assume.
  • `set -e` / `set -u` / `set -o pipefail` interactions and the `||` escape hatch.
  • `wait` semantics, reaping children, race conditions between background-start and resource readiness.

**Out of scope:**

  • Cron scripts and scheduled job reliability (owned by `cron-job-auditor`).
  • Polling, retry, backoff, health-check loops (owned by `condition-based-waiting`).
  • Service health reporting (owned by `service-health-check`).
  • Fish shell configuration (owned by `shell-config`).
  • Shell language features unrelated to process lifecycle -- parameter expansion, arrays, etc.

Reference Loading Table

| Signal | Load These Files | Why | |---|---|---| | starting a background process, `&`, `nohup`, `disown`, `setsid`, daemonize | `starting-processes.md` | Launch-time patterns and fd/session rules | | capturing the child PID, `$!` lies, port still listening after kill | `pid-resolution.md` | How to get the real PID and reconcile with observed state | | trap ordering, SIGTERM/SIGKILL, subshell signal inheritance, `exec` | `signals-and-traps.md` | Signal and trap discipline | | verifying a process is actually gone, lock file still present, port still bound | `cleanup-verification.md` | Kill-and-check pattern | | implementation patterns, detection commands, fix snippets, `set -e` + `||` | `preferred-patterns.md` | Catalog of gotchas with `rg`/`grep` detection and paired fixes |

Instructions

Before implementing any pattern, read the repository CLAUDE.md and search the codebase for existing process-management patterns so the new code matches what is already there. Consistency with existing scripts beats local optimization.

Step 1: Pick the pattern

Walk this decision tree. Pick exactly one pattern per task -- do not pre-emptively wrap a background process in a daemon, and do not add a trap handler for a script that runs for 50ms.

1. Are you starting a new process?
   YES -> Is the parent going to exit before the child?
          YES -> Daemonization (Step 3, load references/starting-processes.md)
          NO  -> Background start (Step 2, load references/starting-processes.md)
   NO  -> Continue

2. Do you need to kill or inspect a process someone else started?
   YES -> PID resolution (Step 4, load references/pid-resolution.md)
   NO  -> Continue

3. Are you writing a supervisor (script that manages children)?
   YES -> Signal + trap discipline (Step 5, load references/signals-and-traps.md)
   NO  -> Continue

4. Are you finishing a destructive operation (kill, rm, release)?
   YES -> Cleanup verification (Step 6, load references/cleanup-verification.md)
   NO  -> Stop. The task may not belong in this skill.

Step 2: Start a background process

A background process in the same session (terminal open, parent stays alive). Load `references/starting-processes.md` for full rationale.

Minimum discipline:

1. **Redirect all three fds.** `cmd > log 2>&1 < /dev/null &` -- because an un-redirected background process inherits the terminal, and stray stdin reads block forever. 2. **Capture the PID defensively.** `$!` is the last backgrounded job's shell-level PID. If you wrap in `nohup`, you get the nohup PID, not the child. Re-query before acting on it (Step 4). 3. **Decide if `disown` is needed.** `disown $!` removes the job from the shell's job table so the shell does not send SIGHUP when it exits. Needed for scripts that start long-running children and return.

Minimal correct pattern (in-session, parent stays alive):

cmd > /tmp/cmd.log 2>&1 < /dev/null &
pid=$!
kill -0 "$pid
Read more
Ships withvexjoy-agent

Essays and writing behind this toolkit live at vexjoy.com. VexJoy Agent connects plain-English requests to specialist agents, skills, and workflows. /do selects the knowledge and tools needed for your task.

Get the whole plugin

Other skills on vexjoy-agent.