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

如何在Python中启动后台进程?,python,process,daemon,Python,Process,Daemon,我正在尝试将shell脚本移植到可读性更高的python版本。原始shell脚本在后台用“&”启动多个进程(实用程序、监视器等)。如何在python中实现相同的效果?我希望这些进程不会在python脚本完成后消亡。我确信它与守护进程的概念有某种联系,但我找不到如何轻松做到这一点。您可能想知道答案 最简单的方法是使用操作系统功能,例如: import os os.system("some_command &") 基本上,无论您传递给系统函数的是什么,都会像在脚本中将其传递给shell一样

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

您可能想知道答案

最简单的方法是使用操作系统功能,例如:

import os
os.system("some_command &")

基本上,无论您传递给
系统
函数的是什么,都会像在脚本中将其传递给shell一样执行。

注意:这个答案与2009年发布时相比没有那么及时。现在建议使用其他答案中显示的
子流程
模块

(请注意,子流程模块为生成新流程和检索其结果提供了更强大的功能;使用该模块比使用这些功能更可取。)


如果希望在后台启动进程,可以使用
system()
并以shell脚本的方式调用它,也可以
spawn
调用它:

import os
os.spawnl(os.P_DETACH, 'some_long_running_command')
(或者,您也可以尝试使用便携性较差的
os.p_NOWAIT
标志)


您可能希望开始研究操作系统模块,以分叉不同的线程(通过打开交互式会话并发出帮助(os))。相关函数是fork和任何exec函数。为了让您了解如何启动,请在执行fork的函数中放入类似的内容(该函数需要将列表或元组“args”作为包含程序名称及其参数的参数;您可能还需要为新线程定义stdin、out和err):

虽然解决方案可行,但较新的处理方式(以及文档推荐的方式)是使用
子流程
模块。对于简单命令,它是等效的,但是如果您想做一些复杂的事情,它提供了更多的选项

您的案例示例:

import subprocess
subprocess.Popen(["rm","-r","some.file"])
这将在后台运行
rm-r some.file
。请注意,对从
Popen
返回的对象调用
.communicate()
将一直阻塞,直到它完成为止,因此如果希望它在后台运行,请不要这样做:

import subprocess
ls_output=subprocess.Popen(["sleep", "30"])
ls_output.communicate()  # Will block for 30 seconds
请参阅文档

另外,有一点需要澄清:这里使用的“背景”纯粹是一个shell概念;从技术上讲,您的意思是希望在等待进程完成时生成一个进程而不阻塞它。但是,我在这里使用了“background”来指代类似shell背景的行为。

我发现:

在windows(win xp)上,父进程在
longstask.py
完成其工作之前不会完成。这不是您在CGI脚本中想要的。这个问题不是Python特有的,在PHP社区中,问题是相同的

解决方案是将
distached_进程
传递到WinAPI中的底层
CreateProcess
函数。如果您碰巧安装了pywin32,则可以从Win32 Process模块导入该标志,否则应自行定义:

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid
使用带有
close\u fds=True
参数的
subprocess.Popen()
,这将允许生成的子进程与Python进程本身分离,并在Python退出后继续运行


使用
线程技术捕获输出并在后台运行

如前所述,如果使用
stdout=
捕获输出,然后尝试
read()
,则进程将阻塞

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

这让我们能够做到这一点

首先,看看如何在本问题中单独执行输出重定向部分:

然后:

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
标准输出每0.5秒更新一次,每两行包含:

0
10
1
11
2
12
3
13
每个日志文件包含给定进程的相应日志

灵感来自:

在Ubuntu 18.04、Python 3.6.7上测试。

您可以使用

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

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

os.fork()
非常有用,但它确实有一个明显的缺点,就是只能在*nix上使用。os.fork的唯一问题是它特定于win32。有关此方法的更多详细信息:您还可以使用
线程处理
:我认为这可能在Windows上工作。备注:您必须指定可执行文件的完整路径。此函数将不使用PATH变量,而使用它的变量在Windows下不可用。直接崩溃python for me.os.P_DETACH已被os.P_NOWAIT替换。建议使用
子进程的人能否给我们一个提示,如何使用
子进程分离进程?我如何使用python脚本(比如说attach.py)查找后台进程并重定向其IO,以便attach.py可以对后台运行的某个程序进行读/写操作?真正重复的问题是.Cheers;)Hi Artem。请接受,因为(1)更多的投票,(2)
subprocess.Popen()
是自2010年(我们现在是2015年)以来新的推荐方式,(3)这里的重定向也有一个关于
subprocess.Popen()
的公认答案。干杯:-)@olibre事实上答案应该是
subprocess.Popen(“”
,文件由合适的shebang引导。使用bash和python脚本非常适合我(Debian),隐式地
shell
s并在其父进程中生存
stdout
转到与父端相同的终端。因此,这与OPs请求的shell中的
&
非常相似。但是,所有的问题都非常复杂,而一个小测试很快就显示了这一点;)有关背景信息,请参见@Dan:我该怎么做
0
10
1
11
2
12
3
13
import os
pid = os.fork()
if pid == 0:
    Continue to other code ...