问题:如何在Python中启动后台进程?
我正在尝试将Shell脚本移植到可读性更高的python版本。原始的shell脚本在后台使用“&”启动多个进程(实用程序,监视器等)。如何在python中达到相同的效果?我希望这些过程在Python脚本完成后不会消失。我敢肯定它与守护程序的概念有关,但是我找不到如何轻松实现此目的。
回答 0
注意:此答案的最新版本比2009年发布时要少。subprocess现在建议在文档中使用其他答案中显示的模块
(请注意,子流程模块提供了更强大的工具来生成新流程并检索其结果;使用该模块比使用这些功能更可取。)
如果您希望您的进程在后台启动,则可以使用system()与您的Shell脚本相同的方式来使用和调用它,也可以spawn:
import os
os.spawnl(os.P_DETACH, 'some_long_running_command')(或者,您也可以尝试使用便携性较差的os.P_NOWAIT标志)。
回答 1
尽管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请参阅此处的文档。
另外,有一点需要澄清:这里使用的“背景”纯粹是一个外壳概念;从技术上讲,您的意思是希望在等待进程完成时生成一个没有阻塞的进程。但是,我在这里使用“背景”来指代类似外壳背景的行为。
回答 2
您可能需要答案“如何在Python中调用外部命令”。
最简单的方法是使用该os.system函数,例如:
import os
os.system("some_command &")基本上,传递给system函数的所有内容都将与将其传递给脚本中的shell一样执行。
回答 3
我在这里找到这个:
在Windows(win xp)上,父进程longtask.py只有在完成工作后才能完成。这不是您想要的CGI脚本。问题并非特定于Python,在PHP社区中,问题是相同的。
解决方案是将DETACHED_PROCESS 过程创建标志传递给CreateProcesswin API中的基础函数。如果碰巧安装了pywin32,则可以从win32process模块中导入该标志,否则,您应该自己定义它:
DETACHED_PROCESS = 0x00000008
pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid回答 4
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
您可能想开始研究os模块以派生不同的线程(通过打开交互式会话并发出help(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
捕获输出并在后台运行 threading
如本答案所述,如果您使用捕获输出,stdout=然后尝试进行read(),则该过程将阻塞。
但是,在某些情况下您需要这样做。例如,我想启动两个进程,它们通过它们之间的端口进行通信,并将它们的stdout保存到日志文件和stdout中。
该threading模块使我们能够做到这一点。
首先,看看如何在此问题中单独完成输出重定向:Python Popen:同时写入stdout和日志文件
然后:
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每个日志文件都包含给定进程的相应日志。
灵感来源:https://eli.thegreenplace.net/2017/interacting-with-a-long-running-child-process-in-python/
已在Ubuntu 18.04,Python 3.6.7上测试。

