0
  • I have a macbook (default shell is zsh)

  • I have an executable python script (srcript1.py).

  • I use another executable script (called starter) that runs script1.py when my computer starts, the terminal window that runs starter opens and closes automatically and the script1.py process remains running as a separate process. (starter is runed automatically through an instance of a szh shell when my computer is turned on). The original starter script file is as follows:

#!/bin/bash

cd /script1; nohup ./script1.py &

This script works okay and everything is good.

However, I'm trying to understand what happens when I use (&&) instead of (;) in my script. i.e

#!/bin/bash

cd /scritp1 && nohup ./script1.py &

My issue is that this also works but whenever I try to kill my python script's process I notice that the terminal instance of szh or something else (probably a bash process) is in running as a process. I.e if I call ps in terminal I get

ps -ef | grep "script1" 
123 14679     1   0  2:12PM ??       0:00.00 /bin/bash /script1 starter
123 14680 14679   0  2:12PM ??       0:03.46 /PythonFolder/python ./script1.py
123 14690 14683   0  2:12PM ttys000  0:00.00 grep scrip1

For the script starter that uses &&

and

ps -ef | grep "script1" 
123 14644     1   0  2:08PM ??       0:03.46 /PythonFolder/python ./script1.py
123 14652 14647   0  2:08PM ttys000  0:00.00 grep scrip1

For the starter script using ;

Why do I get two processes in one version and only one in the other? I'm trying to understand why does the szh that runs starter creates a child process running my python script when I use && and why does it terminate when my starter script uses ; and leaves the python script running as a single process.

When I run starter with the script that uses && the terminal app appears and closes but leaves two running processes (14679 and 14680 in this case).

If I use the starter script using ; and I want to kill my python.py process I would just call

kill 14644

But if I use the starter script that uses && and I want to kill both processes I noticed that killing the child process or killing the parent and then the child process work, ie

kill 14680

or

kill 14679; kill 14680

I also noted that I can kill the parent process

kill 14679

and my python script would continue running as usual.

2

The key is in how lists of commands work (quoting Bash manual here because you are using #!/bin/bash as the shebang line):

A list is a sequence of one or more pipelines separated by one of the operators ‘;’, ‘&’, ‘&&’, or ‘||’, and optionally terminated by one of ‘;’, ‘&’, or a newline.

Of these list operators, && and || have equal precedence, followed by ; and &, which have equal precedence.

(And a pipeline is a sequence of one or more commands separated by | or |&).

This means that in

cd /script1; nohup ./script1.py &

because of the precedence rules among operators, the shell sees the list cd /script1, terminated by ;, which is then run synchronously in the "main" shell, and the list nohup ./script1.py, terminated by &, which is then run asynchronously in a spawned separate process.

On the other hand, in

cd /scritp1 && nohup ./script1.py &

the shell sees the AND list cd /scritp1 && nohup ./script1.py, terminated by &, and runs the whole of it asynchronously. This requires spawning a new bash process to execute the list itself (in the background), which in turn spawns a separate process for your Python script.

About kill: killing the parent process does not kill its children processes automatically. This is standard shell behavior. In the case of your && list, killing the child process allows its parent to terminate, because all the parent process was doing was to wait for the child process to return.

If you want to conditionally execute nohup ./script1.py based on the success of cd /script1 and avoid spawning a shell for the AND list, you can enclose the second element in curly braces:

cd /scritp1 && { nohup ./script1.py & }

or use a conditional block:

if
  cd /scritp1
then
  nohup ./script1.py &
fi
7
  • I tried running sleep 2 && sleep 4 & but I only get the second command to run asynchronously in the background. Could you please explain a little bit more why this happens? I was expecting all of it to go the background – Rafael García Oct 19 '20 at 23:50
  • My bad I was using zsh shell, what you said works perfectly in Bash, thanks you! – Rafael García Oct 20 '20 at 0:04
  • @RafaelGarcía Yeah, that is explicitly noted in the zsh documentation: see "note the difference from other shells which execute the whole sublist in the background" near the end of the Simple Commands & Pipelines chapter. – fra-san Oct 20 '20 at 9:14
  • I have though about this for a while and I am still a litte bit confused about this, I will edit this question shortly to reflect some interesting things that I found that make this work with simpler code. I still do not see why the original shell terminates and the resulting process is inherited by INIT. I wanted to mention though that this link helped me a lot create unatached processes. Using (... &) creates a child and a grandchild. as child exits INIT <- Grandchil – Rafael García Feb 3 at 4:16
  • What I though is a possible explanation to my problem is that when you have parent, child, and granchild processes and an upper process is only waiting for an inferior process to terminate the parent process terminates and the other are inherited by INIT. This maybe to not have to many redundant parent processes. Maybe I am forcing the child process to not terminate in my code above so at the end the child and granchild processes are both able to run. Is my logic correct? – Rafael García Feb 3 at 5:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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