docs(bash): make the pgrep self-match section about misreading, not just killing

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 <noreply@anthropic.com>
This commit is contained in:
BluscreamandClaude Opus 5 committed 2026-10-02 23:01:57 +02:00
1 parent 5029ce5e8b
commit 1b688d128c
2 files changed
+43 -2

No files matched your search

+5
View File
@@ -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 <unit>`) and, for a bare
process, `pgrep -ax <name>` — never bare `pgrep -f <name>`, 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
+38 -2
View File
@@ -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 <unit>`.
---