From 1b688d128c370de03041db6dd975b9012674f7ea Mon Sep 17 00:00:00 2001 From: Bluscream Date: Fri, 2 Oct 2026 23:01:57 +0200 Subject: [PATCH] docs(bash): make the pgrep self-match section about misreading, not just killing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing warning only covered piping a self-match into kill. The quieter failure is reading your own shell's PID as "the app is running" and refusing to touch its config — which is exactly what happened while rotating webhooks in a closed VRCNext. Adds: always pass -a so a self-match is visible; prefer -ax (name only, never scans arguments) for "is it running?"; -A/--ignore-ancestors as the fix when -f is unavoidable; and why the [b]racket trick is weaker than it looks — the term can appear elsewhere on the same command line. Points at systemctl is-active for supervised units. cli-tooling.md gains a one-line orientation pointer rather than a copy. Co-Authored-By: Claude Opus 5 --- cli-tooling.md | 5 +++++ code-languages/bash.md | 40 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/cli-tooling.md b/cli-tooling.md index 80e160f..9f971ec 100644 --- a/cli-tooling.md +++ b/cli-tooling.md @@ -111,6 +111,11 @@ In rough order of value: files, scoped to the project directory. 4. **The code itself** — `grep` for the symbol before trusting any document that says it is missing. +5. **Whether the thing is already running** — before editing a live app's config or starting a + second copy. Ask the supervisor (`systemctl --user is-active `) and, for a bare + process, `pgrep -ax ` — never bare `pgrep -f `, which matches your own shell and + reads as a false positive. See + [`code-languages/bash.md`](code-languages/bash.md#pgrep--f-matches-its-own-shell). > [!CAUTION] > **Task documents are leads, not facts.** A `TODO.md` entry can be stale, duplicated, or diff --git a/code-languages/bash.md b/code-languages/bash.md index 896bf5f..869dded 100644 --- a/code-languages/bash.md +++ b/code-languages/bash.md @@ -108,8 +108,44 @@ never `grep`/`head`/`tail` it away. Narrow the command instead when output is ge > The command line containing the pattern **is itself a process**, so `pgrep -f foo.conf` > matches the shell running it. Piping that into `kill` kills the running script — which reads > exactly like "the thing I was testing never happened". -> -> Write the pattern so it cannot match itself: `pgrep -f 'my[a]pp\.conf'` + +The quieter failure is **misreading** the result. `pgrep -f VRCNext` prints a PID, you conclude +the app is running, and you refuse to edit its config — when the only match was your own shell. +Agents hit this constantly, because an agent's `bash -c` command line is enormous: it carries a +snapshot path, the whole `eval '...'` of the script, and every argument inside it. + +**Always pass `-a`.** Bare `pgrep` prints naked PIDs, which cannot be sanity-checked; `-a` +prints the full command line, so a self-match is obvious on sight: + +```bash +pgrep -af myapp # 3201245 /bin/bash -c ... eval 'pgrep -af myapp' ... ← that is you +``` + +Then pick the invocation that cannot match you in the first place: + +```bash +pgrep -ax myapp # match the process NAME only; never scans arguments ← prefer this +pgrep -Af myapp # -A/--ignore-ancestors: excludes your own shell and its parents +pgrep -af 'my[a]pp' # the bracket trick: the regex no longer matches its own literal text +``` + +- **`-x` is the strongest and the default choice** when you are asking "is this program + running?". It compares against the command name, so the pattern is never tested against any + argument list — including yours. Reach for `-f` only when you must distinguish two instances + of the same binary by their arguments. +- **`-A` is the right fix when `-f` is unavoidable**, and it is robust where the bracket trick + is not. Confirm it exists before relying on it in a script (`pgrep --help | grep ancestors`); + it is present in procps-ng 4.0.6. +- **The bracket trick is weaker than it looks.** `pgrep -f '[V]RCNext'` can still match the + calling shell, because the word `VRCNext` may appear *elsewhere* on that command line — such + as in a `cd ~/.config/VRCNext` earlier in the same script. The trick only defends against + matching the pattern's own literal text, not against the term occurring anywhere else in the + command being run. + +> [!TIP] +> For a service under systemd, don't use `pgrep` at all — ask the supervisor, which has no +> self-match problem and distinguishes *stopped* from *failed*: +> `systemctl --user is-active `. ---