Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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?
share|improve this question

2 Answers 2

up vote 2 down vote accepted

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

share|improve this answer
    
Apparently the #/usr/bin/env kind of works too. I didn't get an error message any way when I ran it. –  slm Aug 6 '13 at 17:56
    
Yes, I just found out. I had actually not used the ! and much to my surprise it worked. –  terdon Aug 6 '13 at 18:14

pgrep doesn't parse ouput of ps, it will look through /proc/<PID>/status and /proc/<PID>/cmdline to finding matching for given pattern. Do some strace:

getpid()                                = 6572
stat("/proc/self/task", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
openat(AT_FDCWD, "/proc", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
getdents(3, /* 267 entries */, 32768)   = 6792
stat("/proc/1", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
open("/proc/1/status", O_RDONLY)        = 4
read(4, "Name:\tinit\nState:\tS (sleeping)\nT"..., 1023) = 750
close(4)                                = 0
open("/proc/1/cmdline", O_RDONLY)       = 4
read(4, "/sbin/init", 2047)             = 10
close(4)                                = 0
stat("/proc/2", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
open("/proc/2/status", O_RDONLY)        = 4
read(4, "Name:\tkthreadd\nState:\tS (sleepin"..., 1023) = 518
close(4)                                = 0
open("/proc/2/cmdline", O_RDONLY)       = 4
read(4, "", 2047)                       = 0
close(4)                                = 0
stat("/proc/3", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
open("/proc/3/status", O_RDONLY)        = 4
read(4, "Name:\tksoftirqd/0\nState:\tS (slee"..., 1023) = 521
close(4)                                = 0
open("/proc/3/cmdline", O_RDONLY)       = 4
read(4, "", 2047)                       = 0
close(4)                                = 0
stat("/proc/6", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
open("/proc/6/status", O_RDONLY)        = 4
read(4, "Name:\tmigration/0\nState:\tS (slee"..., 1023) = 519
close(4)                                = 0
open("/proc/6/cmdline", O_RDONLY)       = 4
read(4, "", 2047)                       = 0
close(4)                                = 0

By default, pgrep only matches the process name (which is bash in your case). From man pgrep:

 -f     The pattern is normally only matched against the process name.
        When -f is set, the full command line is used.

So in your case, if you want to show script started via env, try using:

pgrep -f foo.sh
share|improve this answer
    
Can you add the actual strace to your answer? Many people aren't that up on how to use strace so it'll make your answer stronger. –  slm Aug 6 '13 at 17:19
    
Thanks, and pgrep -f works perfectly. What is the strace of? The pgrep? –  terdon Aug 6 '13 at 18:13
    
Yes, It's is strace of pgrep. –  cuonglm Aug 7 '13 at 1:12

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.