Запустить и забыть процесс из скрипта Python

Как мне запустить процесс (например, другой скрипт Python) из скрипта Python, чтобы «дочерний» процесс был полностью отделен от «родительского», чтобы родительский процесс мог а) продолжать свой веселый путь, не дожидаясь завершения дочернего процесса и б) может быть завершен без завершения дочернего процесса?

Родитель:

import os

print "Parent started"
os.system("./child.py")
print "Parent finished"

Ребенок:

import time

print "Child started"
time.sleep(10)
print "Child finished"

Запуск parent.py отпечатков:

Parent started
Child started
Child finished
Parent finished

Что я хочу напечатать:

Parent started
Child started
Parent finished
(seconds later)
Child finished

person Sergey    schedule 30.07.2013    source источник
comment
Посмотрите на subprocess модуль, а конкретно на subprocess.Popen(...).pid   -  person sberry    schedule 30.07.2013
comment
@sberry: Хорошо, похоже, что простое использование subprocess.Popen("./child.py") делает именно то, что мне нужно, хотя из документации это совсем не ясно. Если вы добавите свой комментарий в качестве ответа, я буду рад принять его, спасибо.   -  person Sergey    schedule 30.07.2013
comment
В зависимости от платформы вы, вероятно, могли бы добавить & в конце команды оболочки, и это также сработало бы.   -  person kevinsa5    schedule 30.07.2013
comment
@ kevinsa5: да, это работает еще лучше - я только что обнаружил, что с Popen(), если родительский процесс уничтожается, дочерний процесс, похоже, тоже уничтожается.   -  person Sergey    schedule 30.07.2013


Ответы (3)


Поскольку вы упомянули os.system, я думаю, стоит упомянуть, что вы должны были использовать os.spawn* с режимом P_NOWAIT для достижения части «забыть».

Но модуль subprocess предоставляет замену для os.system, os,spawn* и т. д., поэтому вы должны использовать его вместо этого

import subprocess
p = subprocess.Popen("./child.py")
print "pid = ", p.pid

См. раздел Замена os.spawn на subprocess.Popen

Как я объяснил в комментариях, оба процесса parent.py и child.py все еще находятся в одной группе процессов, и поэтому терминал будет пересылать сигналы (например, Ctrl-C) всем процессам в группе процессов переднего плана, поэтому оба будут уничтожены, когда вы Ctrl-C. Поэтому, если вы не хотите этого, вы можете заставить child.py находиться в новой группе процессов со следующим:

#!/usr/bin/env python
import subprocess
import time
import os
p = subprocess.Popen("./child.py", preexec_fn=os.setsid)
print "pid = ", p.pid
time.sleep(30) # Ctrl-C at this point will not kill child.py
print "parent exit"
person RubenLaguna    schedule 10.06.2014
comment
Эй, спасибо за ответ на этот старый вопрос. Однако есть проблема с subprocess.Popen: если родительский процесс завершается, дочерний процесс также завершается. - person Sergey; 11.06.2014
comment
Что ты имеешь в виду? Я попробовал этот фрагмент кода, и дочерний элемент не был прекращен, по крайней мере, если родитель умирает по естественным причинам. Ctrl-C (все сигналы) различны, терминал получает сигнал и пересылает его любому процессу в группе процессов переднего плана. Группа процессов здесь включает в себя parent.py и child.py, поэтому оба уничтожаются. Я попытаюсь предоставить конкретную последовательность Popen для создания дочернего элемента в новой группе процессов. - person RubenLaguna; 11.06.2014

Используя asyncio, вы можете написать простой декоратор как @background

import asyncio
import time

def background(f):
    def wrapped(*args, **kwargs):
        return asyncio.get_event_loop().run_in_executor(None, f, *args, *kwargs)

    return wrapped

@background
def foo():
    time.sleep(1)
    print("foo() completed")

print("Hello")
foo()
print("I didn't wait for foo()")

Производит

>>> Hello
>>> I didn't wait for foo()
>>> foo() completed
person nehem    schedule 12.11.2018

Отвечая на мой собственный вопрос: я просто использовал os.system с & в конце команды, как это было предложено @kevinsa. Это позволяет завершить родительский процесс без завершения дочернего.

Вот код:

child.py

#!/usr/bin/python

import time
print "Child started"
time.sleep(10)
print "Child finished"

parent.py, используя subprocess.Popen:

#!/usr/bin/python

import subprocess
import time

print "Parent started"
subprocess.Popen("./child.py")
print "(child started, sleeping)"

time.sleep(5)

print "Parent finished"

Вывод:

$ ./parent.py
Parent started
(child started, sleeping)
Child started
^CTraceback (most recent call last):
Traceback (most recent call last):
  File "./child.py", line 5, in <module>
  File "./parent.py", line 13, in <module>
        time.sleep(10)
time.sleep(5)
KeyboardInterrupt
KeyboardInterrupt
  • обратите внимание, как дочерний процесс никогда не завершается, если родитель прерывается с помощью Ctrl-C

parent.py, используя os.system и &

#!/usr/bin/python

import os
import time

print "Parent started"
os.system("./child.py &")
print "(child started, sleeping)"

time.sleep(5)

print "Parent finished"

Вывод:

$ ./parent.py
Parent started
(child started, sleeping)
Child started
^CTraceback (most recent call last):
  File "./parent.py", line 12, in <module>
    time.sleep(5)
KeyboardInterrupt

$ Child finished

Обратите внимание, как ребенок живет за пределами Ctrl-C.

person Sergey    schedule 10.06.2014