Bash – Why can’t pgrep find scripts started via env

bashenvprocess-managementps

For example:

$ cat foo.sh
#!/usr/bin/env bash
while true; do sleep 1 ; done
$ ./foo.sh &
$ pgrep foo.sh
$

Contrast with:

$ cat bar.sh
#!/bin/bash
while true; do sleep 1 ; done
$ ./bar.sh &
$ pgrep bar.sh
21202

The process started by env bash shows up in the output of ps aux as:

terdon 4203  0.0  0.0  26676  6340 pts/3    S    17:23   0:00 /bin/bash

while the one started with /bin/bash shows as

terdon  9374  0.0  0.0  12828  1392 pts/3    S    17:27   0:00 /bin/bash ./bar.sh

which probably explains why it the first is not being caught by pgrep. So, questions are:

  • Why does the name of the script not show up when called through env?
  • Does pgrep simply parse the output of ps?
  • Is there any way around this so that pgrep can show me scripts started via env?

Best Answer

Q#1

Why does the name of the script not show up when called through env?

From the shebang wikipedia article:

Under Unix-like operating systems, when a script with a shebang is run as a program, the program loader parses the rest of the script's initial line as an interpreter directive; the specified interpreter program is run instead, passing to it as an argument the path that was initially used when attempting to run the script.

So this means that the name of the script is what's known by the kernel as the name of the process, but then immediately after it's invoked, the loader then execs the argument to #! and passes the rest of the script in as an argument.

However env doesn't do this. When it's invoked, the Kernel knows the name of the script and then executes env. env then searches the $PATH looking for the executable to exec.

It is then env that executes the interpreter. It knows nothing of the original name of the script, only the Kernel knows this. At this point env is parsing the rest of the file and passing it to interpreter that it just invoked.

Q#2

Does pgrep simply parse the output of ps?

Yes, kind of. It's calling the same C libraries that ps is making use of. It's not simply a wrapper around ps.

Q#3

Is there any way around this so that pgrep can show me scripts started via env?

I can see the name of the executable in the ps output.

$ ps -eaf|grep 32405
saml     32405 24272  0 13:11 pts/27   00:00:00 bash ./foo.sh
saml     32440 32405  0 13:11 pts/27   00:00:00 sleep 1

In which case you can use pgrep -f <name> to find the executable, since it will search the entire command line argument, not just the executable.

$ pgrep -f foo
32405

References

Related Question