1

I found ps or pgrep cannot find running script without "#!/bin/bash"

Here is a sample.sh:

while true
do
    echo $(date)                                                                                                                                                         
done

start the script (ubuntu 18.04, Linux version 4.15.0-101-generic):

$echo $BASH
/bin/bash
./sample.sh

open another terminal, ps only find the command grep

$ps -aux |grep sample.sh
16887  0.0  0.0  16184  1008 pts/4    S+   07:12   0:00 grep --color=auto sample

pgrep find nothing

$pgrep sample
$

But if I add "#!/bin/bash" to sample.sh, everything works now:

#!/bin/bash      <-----add this line                                                                                                                                                        
while true
do
    echo $(date)
done

I am wondering why.

3
  • 1
    I guess that depends on the bash version you have, because I can't get sample.sh's PID either way with bash 5.0.11. But if I put printf 'boogaloo' > /proc/self/comm in sample.sh's first line, I can get its PID by pgrep boogaloo. Commented Jun 5, 2020 at 11:30
  • @camino : One difference of course is that without a #! line, the script will be run by sh , but this still does not explain for me the differnce. What happens if you start the script explicitly by sh sample.sh, compared to bash sample.sh? Commented Jun 5, 2020 at 12:27
  • @user1934428 sh sample.sh, ps aux|grep sample.sh works. Commented Jun 5, 2020 at 23:07

1 Answer 1

3

Let's start with the second of your cases, namely where you do have #!/bin/bash, because it is actually the easier one to deal with first.

With the #!/bin/bash

When you execute a script which starts with #!/path/to/interpreter, the Linux kernel will understand this syntax and will invoke the specified interpreter for you in the same way as if you had explicitly added /path/to/interpreter to the start of the command line. So in the case of your script starting with #!/bin/bash, if you look using ps ux, you will see the command line /bin/bash ./sample.sh.

Without the #!/bin/bash

Now turning to the other one where the #!/bin/bash is missing. This case is more subtle.

A file which is neither a compiled executable nor a file starting with the #! line cannot be executed by the Linux kernel at all. Here is an example of trying to run the sample.sh without the #!/bin/bash line from a python script:

>>> import subprocess
>>> p  = subprocess.Popen("./sample.sh")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/subprocess.py", line 394, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1047, in _execute_child
    raise child_exception
OSError: [Errno 8] Exec format error

And to show that this is not just a python issue, here is a demonstration of exactly the same thing, but from a C program. Here is the C code:

#include <stdio.h>
#include <unistd.h>

int main() {

  execl("./sample.sh", "sample.sh", NULL);

  /* exec failed if reached */
  perror("exec failed");
  return 1;  
}

and here is the output:

exec failed: Exec format error

So what is happening here case when you run the script is that because you are invoking it from a bash shell, bash is giving some fault tolerance by running the commands directly after the attempt to "exec" the script has failed.

What is happening in more detail is that:

  • bash forks a subshell,

  • inside the subshell it straight away does a call to the Linux kernel to "exec" your executable, and if successful, that would end that (subshell) process and replace it with a process running the executable

  • however, the exec is not successful, and this means that the subshell is still running

  • at that point the subshell just reads the commands inside your script and starts executing them directly.

The overall effect is very similar to the #!/bin/bash case, but because the subshell was just started by forking your original bash process, it has the same command line, i.e. just bash, without any command line argument. If you look for this subshell in the output of ps uxf (a tree-like view of your processes), you will see it just as

bash
 \_ bash

whereas in the #!/bin/bash case you get:

bash
 \_ /bin/bash ./sample.sh
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.