Как запустить фоновый процесс в Python?
Я пытаюсь портировать сценарий оболочки на гораздо более читаемую версию Python. Оригинальный сценарий оболочки запускает несколько процессов (утилиты, мониторы и т. Д.) В фоновом режиме с помощью "&". Как я могу добиться того же эффекта в Python? Я бы хотел, чтобы эти процессы не умирали после завершения сценариев Python. Я уверен, что это как-то связано с концепцией демона, но я не мог найти способ сделать это легко.
10 ответов
Примечание. Этот ответ является менее актуальным, чем он был опубликован в 2009 году. subprocess
модуль, показанный в других ответах, теперь рекомендуется в документации
(Обратите внимание, что модуль подпроцесса предоставляет более мощные средства для порождения новых процессов и получения их результатов; использование этого модуля предпочтительнее использования этих функций.)
Если вы хотите, чтобы ваш процесс запускался в фоновом режиме, вы можете использовать system()
и назовите его так же, как ваш скрипт, или вы можете spawn
Это:
import os
os.spawnl(os.P_DETACH, 'some_long_running_command')
(или, альтернативно, вы можете попробовать os.P_NOWAIT
флаг).
Смотрите документацию здесь.
В то время как решение jkp работает, более новый способ работы (и способ, рекомендуемый документацией) состоит в том, чтобы использовать subprocess
модуль. Для простых команд это эквивалентно, но он предлагает больше вариантов, если вы хотите сделать что-то сложное.
Пример для вашего случая:
import subprocess
subprocess.Popen(["rm","-r","some.file"])
Это должно бежать rm -r somefile
на заднем фоне. Но будьте осторожны: subprocess.Popen()
только запускает процесс в фоновом режиме, если ничто в скрипте python не зависит от вывода выполняемой команды:
Например, следующая команда не будет работать в фоновом режиме:
import subprocess
ls_output=subprocess.Popen(["ls", "-a"], stdout=subprocess.PIPE)
Смотрите документацию здесь.
Кроме того, пояснение: "Фон" - это просто концепция оболочки: вам, вероятно, нужно создать новый процесс. Я использовал здесь "фон", чтобы ссылаться на поведение, похожее на оболочку, но не принимайте это за процесс, фактически находящийся в фоновом режиме.
Возможно, вам нужен ответ "Как вызвать внешнюю команду в Python".
Самый простой подход заключается в использовании os.system
функция, например:
import os
os.system("some_command &")
По сути, все, что вы передаете system
Функция будет выполнена так же, как если бы вы передали ее в оболочку в скрипте.
Я нашел это здесь:
В 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
Использование 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'
И захватывать вывод и работать на фоне с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.
Возможно, вы захотите начать изучение модуля os для разветвления разных потоков (открыв интерактивный сеанс и выполнив справку (os)). Соответствующими функциями являются 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)
В отличие от некоторых предыдущих ответов, которые используютsubprocess.Popen
, в этом ответе используетсявместо. Проблема с использованиемPopen
заключается в том, что если процесс не дождаться завершения вручную,<defunct>
запись остается в таблице процессов Linux, как видноps
. Эти записи могут суммироваться.
Напротив, в случае с , он по своей конструкции ожидает завершения процесса, поэтому в таблице процессов не останется такой несуществующей записи. Потому чтоsubprocess.run
блокируется, его можно запустить в потоке. Остальная часть кода может продолжаться после запуска этого потока. Таким образом, процесс эффективно выполняется в фоновом режиме.
import subprocess, threading
kwargs = {stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True, **your_kwargs}
threading.Thread(subprocess.call, args=(your_command,), kwargs=kwargs).start()
Ты можешь использовать
import os
pid = os.fork()
if pid == 0:
Continue to other code ...
Это заставит процесс python работать в фоновом режиме.
Я еще не пробовал, но вы не можете использовать файлы .pyw вместо файлов .py? файлы pyw не имеют консоли, поэтому теоретически они не должны появляться и работать как фоновый процесс.