On a Linux host you run `pgrep postgres-exporter` and it prints nothing, even though `ps` clearly shows the process running. What is `pgrep` matching against, and what changes when you add `-f`?
answer
- two names per process, not one
- the kernel's short name is capped
- fifteen characters, then it stops
- -f searches the whole argv
- dry-run with -af before pkill
basics
~20 sBy default pgrep matches the kernel's short process name, which is capped at 15 characters, so a longer name such as postgres-exporter can never match. The -f flag matches the full command line instead, which finds it — and matches much more besides.
solid answer
~50 sBy default `pgrep` matches its pattern against the process name the kernel keeps in `/proc/<pid>/comm`, and that field is limited to 15 characters plus a terminator. `postgres-exporter` is 17 characters, so the stored name is truncated and the pattern never matches — this bites constantly with Go and Java binaries that have long names. Adding `-f` makes pgrep match against the full command line from `/proc/<pid>/cmdline` instead, which finds it. The cost is that the pattern is now an unanchored regular expression tested against every argument of every process, so `pkill -f exporter` can take out things you never intended — a `tail` on the exporter's log, an editor with the config file open, a wrapper script. My habit is to dry-run with `pgrep -af` and read what comes back before ever converting it to a `pkill`, and to anchor or use `-x` for an exact match when I can.
code
bash · 2 linespgrep -af postgres-exporter
pgrep -u postgres -x postgresgo deeper
Know that a process has both a short name and a full command line, and that pgrep looks at the short name unless you pass -f. Say plainly that -f widens what can match.
Be able to state the 15-character limit on the kernel's process-name field, explain that -f turns the pattern into an unanchored regex over the whole argv, and name the flags that narrow it: -x, -u, -a.
Show the operational discipline: dry-run with pgrep -af, narrow by owner rather than by cleverer regex, and prefer the service manager over pkill for anything a supervisor is watching.
Argue for identifying processes by a stable handle — a unit, a cgroup, a pidfile — rather than by pattern matching argv, and treat pattern-based kills in automation as a defect to be designed out.
## Two different strings, two different matches Every process on Linux carries two textual identities: - `/proc/<pid>/comm` — the short **process name**, set from the executable's basename at exec and changeable at runtime. The kernel stores it in a fixed-size buffer of 16 bytes, so it holds at most **15 characters** plus the terminator. This is what `ps` shows in the `comm` column and what `top` shows by default. - `/proc/<pid>/cmdline` — the full **command line**, a NUL-separated list of argv entries. This is `ps`'s `args` column. `pgrep` and `pkill` match against `comm` by default and against `cmdline` with `-f`. That single sentence explains the failure: any pattern longer than 15 characters, or any pattern that only appears in an argument rather than in the binary's name, cannot match without `-f`. A Java service is the extreme case — its `comm` is simply `java`, and everything identifying it lives in the arguments. ``` ps -eo pid,comm,args | head ``` Run that once and the truncation is obvious: the `comm` column is clipped while `args` shows the whole line. ## Why -f is sharper than it looks With `-f`, the pattern is an extended regular expression evaluated against the whole command line, unanchored. Everything on that line is fair game — the interpreter, the script path, every flag, every value. Consequences that show up in real incidents: - `pkill -f python` kills every Python process on the box, including the deployment agent you are standing on. - A pattern that appears in an unrelated process's arguments matches it too: `less /var/log/postgres-exporter.log` matches `-f postgres-exporter`. - A shell that was started with the pattern in its own argv — `bash -c 'restart postgres-exporter'`, or the `ssh` command line you used to get in — matches as well. `pgrep` and `pkill` never report or signal *themselves*, which is the one thing they do protect you from, and they will not signal PID 1. Everything else you have to reason about. ## The flags that make it safe - **`-a`** lists the full command line beside each PID, so `pgrep -af <pattern>` shows you exactly the set `pkill -f <pattern>` would hit. Dry-run first, always. (`-l` gives just the name.) - **`-x`** requires the pattern to match the whole string exactly rather than as a substring. `pgrep -x nginx` will not match `nginx-debug`. - **`-u`** restricts to a user, `-P` to children of a given parent, `-s` to a session — narrowing by ownership is usually safer than making the regex cleverer. - **`-n`** and **`-o`** select only the newest or the oldest match, which is how you target the most recent instance of something. - **`-c`** counts matches instead of listing them, useful in a monitoring one-liner. - On `pkill`, **`-e`** echoes what was killed, giving you an after-the-fact record, and the signal is chosen the same way as with `kill` — `pkill -HUP nginx` to reload rather than terminate. ## Why not just ps | grep The `ps aux | grep foo` idiom has a well-known defect: the `grep` process's own command line contains `foo`, so it matches itself, and any script that counts the lines is off by one. The traditional dodge is to bracket a character, `grep [f]oo`, so the pattern text no longer equals the string being searched. `pgrep` removes the class of problem, returns PIDs directly rather than text you have to cut columns out of, and gives a meaningful exit status: 0 if something matched, 1 if nothing did. That makes it the right primitive when you are deciding whether to act. ## The pkill discipline Because `pkill -f` is a pattern that expands into a kill list at the moment it runs, the set it hits is whatever happens to be running then — not what was running when you tested it. Treat it as you would `rm -rf` with a glob: 1. `pgrep -af '<pattern>'` and read every line. 2. Narrow with `-u`, `-x`, or an anchored pattern until only intended processes remain. 3. Only then run the same pattern under `pkill`, and prefer a service manager (`systemctl restart`) when the process is a managed service, because killing it directly can race the supervisor's own restart handling. If you can state the `comm`-versus-`cmdline` distinction, name the 15-character limit, and describe the dry-run habit, you have shown the interviewer that you have been burned by this once and learned from it.
- Why does `pgrep java` return several PIDs that belong to completely different services?Because the process name for every JVM is just `java` — the identity of the service lives entirely in the arguments: the classpath, the jar, the main class, the system properties. Matching on the name cannot distinguish them. You need `pgrep -af` with a pattern from the argument list, or better, ask the service manager which unit owns which PID.
- What is the safer alternative to `pkill -f` when the target is a managed service?Ask the supervisor. `systemctl stop` or `systemctl restart` acts on the unit's whole control group, stops the supervisor from immediately restarting what you just killed, and gets the ordering and timeouts right. `pkill` fights the supervisor: it kills a process the manager is watching, which may be restarted underneath you or leave the unit in a failed state.
- Does pgrep ever match itself, and why does that matter?No — pgrep and pkill explicitly exclude their own process from the results, and pkill refuses to signal PID 1. That removes the classic `ps | grep` self-match bug where a count comes back one too high. It does not protect you from matching the shell or ssh session that happens to have the pattern in its own command line.
saying these in an interview costs you the question
- Believes pgrep matches the full command line by default
- Says the 15-character limit is a pgrep restriction
- Runs pkill -f without listing matches first
- Thinks pgrep can match itself like ps | grep does
- Uses pkill on a systemd-managed service instead of systemctl