如何在Python中启动后台进程?

时间:2009-07-28 18:56:50

标签: python process daemon

我正在尝试将shell脚本移植到更易读的python版本。原始shell脚本在后台使用“&”启动多个进程(实用程序,监视器等)。如何在python中实现相同的效果?我希望这些进程不会在python脚本完成时死掉。我确信它与守护进程的概念有某种关系,但我无法轻易找到如何做到这一点。

8 个答案:

答案 0 :(得分:317)

虽然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)

请参阅文档here

另外,澄清一点:“背景”纯粹是一个shell概念:你可能想要的是产生一个新的进程。我在这里使用“背景”来引用类似shell的背景行为,但是不要将其误认为是实际存在于后台的进程。

答案 1 :(得分:69)

注意:这个答案比2009年发布时更少。现在建议使用其他答案中显示的subprocess模块in the docs

  

(请注意,子进程模块提供了更强大的工具来生成新进程并检索其结果;使用该模块比使用这些函数更可取。)


如果您希望自己的流程在后台启动,可以使用system()并以与shell脚本相同的方式调用它,也可以spawn

import os
os.spawnl(os.P_DETACH, 'some_long_running_command')

(或者,您可以尝试使用不太便携的os.P_NOWAIT标志)。

请参阅documentation here

答案 2 :(得分:37)

您可能想要"How to call an external command in Python"的答案。

最简单的方法是使用os.system函数,例如:

import os
os.system("some_command &")

基本上,无论您传递给system函数的任何内容都将执行,就像您在脚本中将其传递给shell一样。

答案 3 :(得分:26)

我发现了here

在Windows(win xp)上,父进程在longtask.py完成工作之前不会完成。这不是你想要的CGI脚本。问题不是Python特有的,在PHP社区中问题是一样的。

解决方案是将DETACHED_PROCESS Process Creation Flag传递给win API中的基础CreateProcess函数。如果你碰巧安装了pywin32,你可以从win32process模块​​导入标志,否则你应该自己定义:

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid

答案 4 :(得分:18)

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'

答案 5 :(得分:11)

您可能希望开始调查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)

答案 6 :(得分:3)

两者均捕获输出并使用threading

在后台运行

on this answer所述,如果您使用stdout=捕获了输出,然后尝试进行read(),则过程将阻塞。

但是,在某些情况下您需要这样做。例如,我想启动two processes that talk over a port between them,并将其标准输出保存到日志文件和标准输出中。

threading module允许我们这样做。

首先,看看这个问题中如何单独执行输出重定向:Python Popen: Write to stdout AND log file simultaneously

然后:

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上进行了测试。

答案 7 :(得分:0)

你可以使用

import os
pid = os.fork()
if pid == 0:
    Continue to other code ...

这将使python进程在后台运行。