ps/pgrep cannot find running script missing #!/bin/bash - linux

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.

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

Related

Single PID run two commands

When I check the process from the bash, it display:
In [42]: !ps
PID TTY TIME CMD
417 ttys000 0:00.49 -bash
7783 ttys000 0:06.50 /Users/me/anaconda3/bin/python /Users/me/anaconda3/bin/ipython
It seems that pid 7783 run two commands simultaneous,
Could please provide any hints help understand it?
It is running only one command but with one argument:
/Users/me/anaconda3/bin/python /Users/me/anaconda3/bin/ipython
^ command ^ argument
Python scripts are not directly executable. An interpreter is required to actually run them. Similarly, in your case the command is the python interpreter, and the argument is the ipython script.
When you directly execute the script, the operating system peeks inside to see if it has a shebang. This is a line starting with #! (actually the byte sequence 0x2321) followed by the path to the program meant to run the file. For example, on my system the ipython script points at the python3.7 interpreter:
$ head -1 $(which ipython3)
#!/usr/local/opt/python/bin/python3.7
Calling the script automatically expands to calling the shebang program with the script. Thus, you never see the actual script being run by itself - only the interpreter running the script.
$ ipython3 -c '!ps' | grep ipython3
5764 ttys004 0:00.37 /usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/Resources/Python.app/Contents/MacOS/Python /Users/miyagi/Library/Python/3.7/bin/ipython3 -c !ps

RedHat Enterprise 7.2 init.d runlevel 0 script cannot find file in /home directory

I have an etc/init.d script that tries to call a shell script in a /home/myuser directory. One shell script for startup, another shell script for shutdown.
The start script gets called just fine ( from rc3.d/S99cslink , rc5.d/S99cslink ) but when I try to call the stop script ( from rc0.d/K01cslink ) I get the message that /home/myuser/bin/stop_service.sh cannot be found.
/etc/rc.d/init.d/cslink: line 42: /home/myuser/bin/stop_service.sh: No such file or directory
I verified that at the point in time when I am trying to run /home/myuser/bin/stop_service.sh , /home/myuser is unavailable (an ls -l /home/myuser >/tmp/mylog.log 2>&1 from inside the init.d script shows the error
ls: cannot access /home/myuser: No such file or directory
Both the start) script and the stop) script in init.d are run with runuser -l myuser, so I don't think it's a permissions problem.
Why would /home/myuser be unavailab.e, and can I run my script at a different point in time when /home/myuser is still available?
All the answers I see through searching are saying that I should check for Windows-style carriage returns in my stop_service.sh script, but I have checked and that's not the issue here.

Shell script doesn't exit if output redirected to logger

I was looking for a way to route the output of my shell scripts to syslog, and found this article, which suggests putting the following line at the top of the script:
exec 1> >(logger -s -t $(basename $0)) 2>&1
I've tried this with the following simple script:
#!/bin/bash
exec 1> >(logger -s -t $(basename $0)) 2>&1
echo "testing"
exit 0
When I run this script from the shell, I do indeed get the message in the syslog, but the script doesn't seem to return--in order to continue interacting with the shell, I need to hit Enter or send a SIGINT signal. What's going on here? FWIW, I'm mostly using this to log the results of cron jobs, so in the wild I probably don't need it to work properly in an interactive shell session, but I'm nervous using something I don't really understand in production. I am mostly worried about spawning a bunch of processes that don't terminate cleanly.
I've tested this on Ubuntu 15.10, Ubuntu 16.04, and OSX, all with the same result.
Cutting a long story short: the shell script does exit and so does the logger — there isn't actually a problem — but the output from the logger lead to confusion.
Converting comments into an answer.
Superficially, given the symptoms you describe, what's going on is that Bash isn't exiting until all its child processes exit. You could try exec >/dev/null 2>&1 before exit 0 to see if that stops the logger — basically, the redirection closes its inputs, so it should terminate, allowing the script to exit.
However, when I try your script (bash logtest.sh) on macOS Sierra 10.12.2 (though I'd not expect it to change in earlier versions), the command exits promptly and produces a log message on the terminal like this (I use Osiris JL: as my prompt):
Osiris JL: bash logtest.sh
Osiris JL: Dec 26 12:23:50 logtest.sh[6623] <Notice>: testing
Osiris JL: ps
PID TTY TIME CMD
71792 ttys000 0:00.25 -bash
534 ttys002 0:00.57 -bash
543 ttys003 0:01.71 -bash
558 ttys004 0:00.44 -bash
Osiris JL:
I hit return on the blank line and got the prompt before the ps command.
Note that the message from logger arrived after the prompt.
When I ran bash logtest.sh (where logtest.sh contained your script), the only key I hit was the return to enter the command (which the shell read before running the command). I then got a prompt, the output from logger, and a blank line with the terminal waiting for input. That's normal. The logger was not still running — I could check that in other windows.
Try typing ls instead of just hitting return. The shell is waiting for input. It wrote its prompt, but the logger output confused the on-screen layout. For me, I got:
Osiris JL: bash logtest.sh
Osiris JL: Dec 26 13:28:28 logtest.sh[7133] <Notice>: testing
ls
README.md ix37.sql mq13.c sh11.o
Safe lib mq13.dSYM so-4018-8770
Untracked ll89 oddascevendesc so-4018-8770.c
ci11 ll89.cpp oddascevendesc.c so-4018-8770.dSYM
ci11.c ll89.dSYM oddascevendesc.dSYM sops
ci11.dSYM ll97 rav73 src
data ll97.c rav73.c tf17
doc ll97.dSYM rav73.dSYM tf17.cpp
es.se-36764 logtest.sh rd11 tf17.dSYM
etc mac-clock-get-time rd11.c tf19
fa37.sh mac-clock-get-time.c rd11.dSYM tf19.c
fileswap.sh mac-clock-get-time.dSYM rn53 tf19.dSYM
gm11 makefile rn53.c x-paste.c
gm11.c matrot13 rn53.dSYM xc19
gm11.dSYM matrot13.c sh11 xc19.c
inc matrot13.dSYM sh11.c xc19.dSYM
infile mq13 sh11.dSYM
Osiris JL:

Script command losing alias from shell

When I run the script command it loses all the aliases from the existing shell which is not desired for people using lots of aliases. So, I am trying to see if I can automatically source the .profile again to see if it works without the user have to do it.
Here below is the code:
#!/bin/bash -l
rm aliaspipe
mkfifo aliaspipe
bash -c "sleep 1;echo 'source ~/.bash_profile' > aliaspipe ;echo 'alias' > aliaspipe ;echo 'exec 0<&-' > aliaspipe"&
echo "starting script for recording"
script < aliaspipe
Basically I am creating a named pipe and the making the pipe as stdin to the script program, trying to run the source command and then close the stdin from pipe to the terminal stdin so that I can continue with the script.
But when I execute, the script is exiting after I execute "exec 0<&-",
bash-3.2$ exec 0<&-
bash-3.2$ exit
Script done, output file is typescript
Not sure why the exit is called and script is terminated. If I can make the script move the stdin from pipe to terminal then it should be fine.
You can get script to execute a bash login shell by telling it to do so explicitly.
# Gnu script (most Linux distros)
script -c "bash -l"
# BSD script (also Mac OS X)
script typescript bash -l
That will cause your .bash_profile to be sourced.
By the way, redirections are not stacks. When you write exec 0<&-, you're closing standard input, and when bash's standard input is closed, it exits.

Run a script in the same shell(bash)

My problem is specific to the running of SPECCPU2006(a benchmark suite).
After I installed the benchmark, I can invoke a command called "specinvoke" in terminal to run a specific benchmark. I have another script, where part of the codes are like following:
cd (specific benchmark directory)
specinvoke &
pid=$!
My goal is to get the PID of the running task. However, by doing what is shown above, what I got is the PID for the "specinvoke" shell command and the real running task will have another PID.
However, by running specinvoke -n ,the real code running in the specinvoke shell will be output to the stdout. For example, for one benchmark,it's like this:
# specinvoke r6392
# Invoked as: specinvoke -n
# timer ticks over every 1000 ns
# Use another -n on the command line to see chdir commands and env dump
# Starting run for copy #0
../run_base_ref_gcc43-64bit.0000/milc_base.gcc43-64bit < su3imp.in > su3imp.out 2>> su3imp.err
Inside it it's running a binary.The code will be different from benchmark to benchmark(by invoking under different benchmark directory). And because "specinvoke" is installed and not just a script, I can not use "source specinvoke".
So is there any clue? Is there any way to directly invoke the shell command in the same shell(have same PID) or maybe I should dump the specinvoke -n and run the dumped materials?
You can still do something like:
cd (specific benchmark directory)
specinvoke &
pid=$(pgrep milc_base.gcc43-64bit)
If there are several invocation of the milc_base.gcc43-64bit binary, you can still use
pid=$(pgrep -n milc_base.gcc43-64bit)
Which according to the man page:
-n
Select only the newest (most recently started) of the matching
processes
when the process is a direct child of the subshell:
ps -o pid= -C=milc_base.gcc43-64bit --ppid $!
when not a direct child, you could get the info from pstree:
pstree -p $! | grep -o 'milc_base.gcc43-64bit(.*)'
output from above (PID is in brackets): milc_base.gcc43-64bit(9837)

Resources