Как запустить фоновый процесс в Python?
Я пытаюсь перенести оболочку script в гораздо более читаемую версию python. Исходная оболочка script запускает несколько процессов (утилит, мониторов и т.д.) В фоновом режиме с помощью "&". Как я могу добиться такого же эффекта в python? Я бы хотел, чтобы эти процессы не умирали, когда скрипты python завершались. Я уверен, что это как-то связано с концепцией демона, но я не мог найти, как это сделать легко.
Ответы
Ответ 1
Примечание. Этот ответ менее актуальен, чем когда он был опубликован в 2009 году. Теперь рекомендуется использовать модуль subprocess
, показанный в других ответах в документах
(Обратите внимание, что модуль подпроцесса обеспечивает более мощные возможности для создания новых процессов и получения их результатов, использование этого модуля предпочтительнее использования этих функций.)
Если вы хотите, чтобы ваш процесс начинался в фоновом режиме, вы можете использовать system()
и вызывать его так же, как ваша оболочка script, или вы можете spawn
его:
import os
os.spawnl(os.P_DETACH, 'some_long_running_command')
(или, альтернативно, вы можете попробовать менее переносимый флаг os.P_NOWAIT
).
См. документацию здесь.
Ответ 2
В то время как решение jkp работает, более новый способ работы (и способ, рекомендованный документацией) заключается в использовании модуля subprocess
. Для простых команд это эквивалентно, но предлагает больше возможностей, если вы хотите сделать что-то сложное.
Пример для вашего случая:
import subprocess
subprocess.Popen(["rm","-r","some.file"])
Это запустит rm -r somefile
в фоновом режиме. Обратите внимание, что вызов .communicate()
для объекта, возвращенного из Popen
, будет блокироваться до его завершения, поэтому не делайте этого, если хотите, чтобы он работал в фоновом режиме:
import subprocess
ls_output=subprocess.Popen(["sleep", "30"])
ls_output.communicate() # Will block for 30 seconds
Смотрите документацию здесь.
Кроме того, пояснение: "Фон", как вы его здесь используете, является чисто концепцией оболочки; технически, вы имеете в виду, что хотите запустить процесс без блокировки, пока вы ждете его завершения. Тем не менее, я использовал здесь "background" для обозначения поведения, похожего на shell-background.
Ответ 3
Вероятно, вам нужен ответ "Как вызвать внешнюю команду в Python" .
Самый простой способ - использовать функцию os.system
, например:
import os
os.system("some_command &")
В принципе, все, что вы передаете функции system
, будет выполняться так же, как если бы вы передали его оболочке в script.
Ответ 4
Я нашел это здесь:
В windows (win xp) родительский процесс не завершится, пока longtask.py
не завершит свою работу. Это не то, что вы хотите в CGI-скрипте. Проблема не специфична для Python, в сообществе PHP проблемы одинаковы.
Решение состоит в том, чтобы передать DETACHED_PROCESS
Флаг создания процесса в базовую функцию CreateProcess
в win API. Если вы установили pywin32, вы можете импортировать флаг из модуля win32process, в противном случае вы должны определить его самостоятельно:
DETACHED_PROCESS = 0x00000008
pid = subprocess.Popen([sys.executable, "longtask.py"],
creationflags=DETACHED_PROCESS).pid
Ответ 5
Используйте subprocess.Popen()
с параметром close_fds=True
, что позволит отделить порожденный подпроцесс от самого процесса Python и продолжить работу даже после выхода из Python.
https://gist.github.com/yinjimmy/d6ad0742d03d54518e9f
import os, time, sys, subprocess
if len(sys.argv) == 2:
time.sleep(5)
print 'track end'
if sys.platform == 'darwin':
subprocess.Popen(['say', 'hello'])
else:
print 'main begin'
subprocess.Popen(['python', os.path.realpath(__file__), '0'], close_fds=True)
print 'main end'
Ответ 6
Вероятно, вы захотите начать изучение модуля os для разветвления различных потоков (путем открытия интерактивного сеанса и выдачи справки (os)). Соответствующими функциями являются fork и любой из exec. Чтобы дать вам представление о том, как начать, поместите что-то вроде этого в функцию, которая выполняет fork (функции нужно взять список или кортеж "args" в качестве аргумента, который содержит имя программы и ее параметры, вы также можете захотеть для определения stdin, out и err для нового потока):
try:
pid = os.fork()
except OSError, e:
## some debug output
sys.exit(1)
if pid == 0:
## eventually use os.putenv(..) to set environment variables
## os.execv strips of args[0] for the arguments
os.execv(args[0], args)
Ответ 7
И захватывать вывод и работать на фоне с threading
Как уже упоминалось в этом ответе, если вы перехватываете вывод с помощью stdout=
а затем пытаетесь read()
, тогда процесс блокируется.
Однако есть случаи, когда вам это нужно. Например, я хотел запустить два процесса, которые общаются через порт между ними, и сохранить их стандартный вывод в файл журнала и стандартный вывод.
Модуль threading
позволяет нам это делать.
Во-первых, рассмотрим, как выполнить часть перенаправления вывода в этом вопросе: Python Popen: одновременная запись в стандартный вывод и файл журнала
Затем:
main.py
#!/usr/bin/env python3
import os
import subprocess
import sys
import threading
def output_reader(proc, file):
while True:
byte = proc.stdout.read(1)
if byte:
sys.stdout.buffer.write(byte)
sys.stdout.flush()
file.buffer.write(byte)
else:
break
with subprocess.Popen(['./sleep.py', '0'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc1, \
subprocess.Popen(['./sleep.py', '10'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc2, \
open('log1.log', 'w') as file1, \
open('log2.log', 'w') as file2:
t1 = threading.Thread(target=output_reader, args=(proc1, file1))
t2 = threading.Thread(target=output_reader, args=(proc2, file2))
t1.start()
t2.start()
t1.join()
t2.join()
sleep.py
#!/usr/bin/env python3
import sys
import time
for i in range(4):
print(i + int(sys.argv[1]))
sys.stdout.flush()
time.sleep(0.5)
После запуска:
./main.py
stdout обновляется каждые 0,5 секунды для каждых двух строк:
0
10
1
11
2
12
3
13
и каждый файл журнала содержит соответствующий журнал для данного процесса.
Вдохновленный: https://eli.thegreenplace.net/2017/interacting-with-a-long-running-child-process-in-python/
Протестировано на Ubuntu 18.04, Python 3.6.7.