Как запустить фоновый процесс в 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 не имеют консоли, поэтому теоретически они не должны появляться и работать как фоновый процесс.

Другие вопросы по тегам