19

When I use either of these commands with an argument as the name of a process, both of them return the exact same number. Are they the same commands? Are they two different commands that do the same thing? Is one of them an alias to the other?

pidof firefox
pgrep firefox
Galaxy
  • 505

3 Answers3

24

The programs pgrep and pidof are not quite the same thing, but they are very similar. For example:

$ pidof 'firefox'
5696
$ pgrep '[i]ref'
5696
$ pidof '[i]ref'
$ printf '%s\n' "$?"
1

As you can see, pidof failed to find a match for [i]ref. This is because pidof program returns a list of all process IDs associated with a program called program. On the other hand, pgrep re returns a list of all process IDs associated with a program whose name matches the regular expression re.

In their most basic forms, the equivalence is actually:

$ pidof 'program'
$ pgrep '^program$'

As yet another concrete example, consider:

$ ps ax | grep '[w]atch'
   12 ?        S      0:04 [watchdog/0]
   15 ?        S      0:04 [watchdog/1]
   33 ?        S<     0:00 [watchdogd]
18451 pts/5    S+     0:02 watch -n600 tail log-file
$ pgrep watch
12
15
33
18451
$ pidof watch
18451
Fox
  • 8,193
8

Fox has mentioned that pgrep searches using regular expressions, while pidof does not.

But pgrep also has a lot more options available:

  • With -u "$UID" you can match only processes belonging to the current user.
  • With --parent you can find the child processes of a given process.
  • You can select the --oldest or --newest of the matching processes.
  • ...and various others listed on the man page...

Let's find out which package each process belongs to (on apt systems):

$ dpkg -S "$(which pidof)"
sysvinit-utils: /bin/pidof

$ dpkg -S "$(which pgrep)" procps: /usr/bin/pgrep

On pacman systems:

$ pacman -Qo "$(which pidof)"
/usr/bin/pidof is owned by procps-ng 3.3.17-1

$ pacman -Qo "$(which pgrep)" /usr/bin/pgrep is owned by procps-ng 3.3.17-1

On RPM systems:

$ rpm -qf "$(which pidof)"
... ?
joeytwiddle
  • 1,018
  • 9
  • 15
2

Just a note: pidof MAY treat everything after the last slash as the program name:

fang@debian:~$ ps -ax | grep k3s | head -n1
    528 ?        Ssl  634:37 /usr/local/bin/k3s server
fang@debian:~$ pgrep k3s
528
fang@debian:~$ pidof k3s
fang@debian:~$ pidof /usr/local/bin/k3s
fang@debian:~$ pidof '/usr/local/bin/k3s server'
528
fang@debian:~$ pidof 'k3s server'
528

Update: As Stéphane Chazelas said, pidof actually match any of:

  • Name: field in /proc/pid/status
  • the first argument in /proc/pid/cmdline
  • the base name of the first argument in /proc/pid/cmdline

In most cases, there should be a NUL after each arg. eg. sleep:

fang@debian:~$ sleep infinity &
[1] 2076966
fang@debian:~$ pidof sleep
2076966
fang@debian:~$ xxd /proc/2076966/cmdline
00000000: 736c 6565 7000 696e 6669 6e69 7479 00    sleep.infinity.
fang@debian:~$ head -n1 /proc/2076966/status
Name:   sleep

It is a bit special because the argv[0] contains a whitespace:

fang@debian:~$ ls -l /usr/local/bin/k3s
-rwxr-xr-x 1 root root 54001664  7月  3 20:16 /usr/local/bin/k3s
fang@debian:~$ head -n1 /proc/528/status
Name:   k3s-server
fang@debian:~$ head -n1 /proc/528/cmdline && echo
/usr/local/bin/k3s server
fang@debian:~$ xxd /proc/528/cmdline 
00000000: 2f75 7372 2f6c 6f63 616c 2f62 696e 2f6b  /usr/local/bin/k
00000010: 3373 2073 6572 7665 7200 0000 0000 0000  3s server.......

In conclusion, to get its pid, you can:

fang@debian:~$ pgrep k3s-server
528
fang@debian:~$ pidof k3s-server
528
fang@debian:~$ pidof 'k3s server'
528

You cannot:

fang@debian:~$ pgrep 'k3s server'

Because it only match

  • Name: field in /proc/pid/status
  • (Seems it is the same to the part inside (...) in /proc/pid/stat, right?)
  • Note that pgrep matches on the process name (the Name: field in /proc/pid/status or the part inside (...) in /proc/pid/stat as show with ps -o comm) by default while pidof matches on the argv[0] (or its basename as you rightly point out) of the last execve() the process or its ancestor made (/proc/pid/cmdline, ps -o args), falling back to the process name only if argv[0] is empty or there's no args (execve() called with an empty arg list, or kernel tasks that have never run a execve()). pgrep -f matches on the arg list (joined with spaces) – Stéphane Chazelas Aug 01 '23 at 05:51
  • On my system (Debian testing where pidof is from sysvinit-utils version 3.07), pidof only matches on the process name if argv[0] is empty or unset. After exec -a foo sleep 20 &, pidof foo matches but not pidof sleep. While with exec -a '' sleep 20, pidof sleep matches but pidof '' doesn't. – Stéphane Chazelas Aug 01 '23 at 13:01
  • Name: in /proc/pid/status is the same as part inside (...) in /proc/pid/stat which is the process name (note: it's limited to 15 bytes on Linux), except that in /proc/pid/status some characters are encoded using backslash escape sequences. For instance newline is represented as \n. For the text that pgrep regexps match against, it seems those characters are replaced with ?. For pidof I don't know. – Stéphane Chazelas Aug 01 '23 at 13:11