How to run nohup command from Python script?

Question:

I have a simple question. I have tried to search for a solution but there are no answers which would explain what I need.

The question is:
How do I start a nohup command from Python? Basically the idea is, that I have a Python script which prepares my environment and I need it to launch multiple scripts with nohup commands. How do I start a nohup command like nohup python3 my_script.py & from within a running Python script to have that nohup command running even after I log out?

Thank you

Asked By: neisor

||

Answers:

You do not need nohup — not even in shell, and even less so in Python. It does the following things:

  • Configures the HUP signal to be ignored (rarely relevant: if a process has no handles on a TTY it isn’t going to be notified when that TTY exits regardless; the shell only propagates signals to children in interactive mode, not when running scripts).
  • If stdout is a terminal, redirects it to nohup.out
  • If stderr is a terminal, redirects it to wherever stdout was already redirected.
  • Redirects stdin to /dev/null

That’s it. There’s no reason to use nohup to do any of those things; they’re all trivial to do without it:

  • </dev/null redirects stdin from /dev/null in shell; stdin=subprocess.DEVNULL does so in Python.
  • >nohup.out redirects stdout to nohup.out in shell; stdout=open('nohup.out', 'w') does so in Python.
  • 2>&1 makes stderr go to the same place as stdout in shell; stderr=subprocess.STDOUT does so in Python.

Because your process isn’t attached to the terminal by virtue of the above redirections, it won’t implicitly get a HUP when that terminal closes. If you’re worried about a signal being sent to the parent’s entire process group, however, you can avoid that by splitting off the child into a separate one:

  • The subprocess.Popen argument start_new_session=True splits the child process into a separate group from the parent in Python, so a parent sent to the process group of the parent as a whole will not be received by the child.
  • Adding a preexec_fn with signal.signal(signal.SIGHUP, signal.SIG_IGN) is even more explicit that the child should by default ignore a SIGHUP even if one is received.

Putting this all together might look like (if you really do want logs to go to a file named nohup.out — I would suggest picking a better name):

import subprocess, signal
subprocess.Popen(['python3', 'my_script.py'],
                 stdin=subprocess.DEVNULL,
                 stdout=open('nohup.out', 'w'),
                 stderr=subprocess.STDOUT,
                 start_new_session=True,
                 preexec_fn=(lambda: signal.signal(signal.NOHUP, signal.SIG_IGN)))

Be sure to change ‘signal.NOHUP’ in the example to ‘signal.SIGHUP’, otherwise you’ll get a strange error.

Answered By: Charles Duffy
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.