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 `. ---