在 Python 中启动后台进程

2024-12-04 08:56:00
admin
原创
140
摘要:问题描述:我正在尝试将 shell 脚本移植到更易读的 python 版本。原始 shell 脚本使用“&”在后台启动多个进程(实用程序、监视器等)。如何在 python 中实现相同的效果?我希望这些进程在 python 脚本完成时不会终止。我确信它以某种方式与守护进程的概念相关,但我找不到如何轻松做到这一点...

问题描述:

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


解决方案 1:

虽然jkp的解决方案有效,但较新的处理方式(也是文档推荐的方式)是使用模块subprocess。对于简单的命令,它是等效的,但如果您想执行一些复杂的事情,它会提供更多选项。

您的案例示例:

import subprocess
subprocess.Popen(["rm","-r","some.file"])

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

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

请参阅此处的文档。

另外,需要澄清一点:您在此处使用的“后台”纯粹是 shell 概念;从技术上讲,您的意思是您希望在等待进程完成时不阻塞地生成进程。但是,我在此处使用“后台”来指代类似 shell 后台的行为。

解决方案 2:

注意:这个答案比 2009 年发布时更新得慢。subprocess现在在文档中建议使用其他答案中显示的模块

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


如果您希望您的进程在后台启动,您可以system()按照与 shell 脚本相同的方式使用和调用它,或者您可以spawn

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

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

请参阅此处的文档。

解决方案 3:

您可能想知道“如何在 Python 中调用外部命令”的答案。

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

import os
os.system("some_command &")

基本上,无论您传递给system函数什么,都会像在脚本中传递给 shell 一样执行。

解决方案 4:

subprocess.Popen()与参数一起使用close_fds=True,这将允许生成的子进程与 Python 进程本身分离,并且在 Python 退出后仍继续运行。

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:

我在这里找到了这个:

在 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

解决方案 6:

都捕获输出并在后台运行threading

正如在这个答案中提到的,如果您使用捕获输出stdout=,然后尝试read(),那么该过程就会被阻止。

但是,有些情况下你需要这样做。例如,我想启动两个通过端口进行通信的进程,并将它们的标准输出保存到日志文件和标准输出中。

threading模块允许我们这样做。

首先,看一下这个问题中如何单独执行输出重定向部分:Python Popen: 同时写入 stdout 和日志文件

然后:

主程序

#!/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, \n     subprocess.Popen(['./sleep.py', '10'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc2, \n     open('log1.log', 'w') as file1, \n     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()

睡眠.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:

您可能想要开始研究用于分叉不同线程的 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)

解决方案 8:

与之前使用 的一些答案不同subprocess.Popen,本答案使用subprocess.run。使用 的问题Popen在于,如果没有手动等待进程完成,<defunct>Linux 进程表中会保留一个过时的条目,如 所示ps。这些条目可以累加。

与 相比Popen,使用 时subprocess.run,根据设计,run它会等待进程完成,因此进程表中不会保留此类已停用的条目。由于subprocess.run是阻塞的,因此它可以在线程中运行。启动此线程后,其余代码可以继续。这样,进程实际上在后台运行。

import subprocess, threading

kwargs = {stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True, **your_kwargs}
threading.Thread(subprocess.run, args=(your_command,), kwargs=kwargs).start()

注意subprocess.call也要等待过程完成,并且可以类似地使用。

解决方案 9:

您可以使用

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

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

解决方案 10:

我在 Linux 上运行的是 Python 3.9.14。我发现在类似情况下,以下方法对我有用:

import subprocess

cmd = "sleep 5 && ls /tmp >& ls.out &"
try: 
    runResult = subprocess.run(["bash", "-c", cmd])
except Exception as ex:
    print( f"Failed to run '{cmd}'" )
    if hasattr( ex, "message" ):
        print( ex.message )
    elif hasattr( ex, "strerror" ):
        print( ex.strerror)
    else:
        print( ex )

如果您运行上述命令并在当前目录中快速执行 ls,您会发现“ls.out”文件尚不存在。再等几秒钟,文件就会出现。因此,命令在 Python 退出后继续运行。

“runResult” 有一个“returncode”字段,指示程序是否成功启动。我不知道稍后在 Python 中终止该进程的好方法。

我还可以采用更 Python-ish(Python-ly?)的方法:我有一个名为“runs5secs”的 shell 脚本:

#!/bin/bash
sleep 5
ls

我可以使用以下命令在后台运行它:

import shlex
import subprocess

cmd = "sleep 5 && ls /tmp >& ls.out &"

logName = "./run5secs.out"
cmd = "./run5secs my1 your2"

try:
  f = open( logName, 'w' )
except Exception as ex:
    print( f"Failed to run '{cmd}'" )
    if hasattr( ex, "message" ):
        print( ex.message )
    elif hasattr( ex, "strerror" ):
        print( ex.strerror)
    else:
        print( ex )

args = shlex.split( cmd )

try: 
    cmdRes = subprocess.Popen( args, stdout=f,
                               stderr=subprocess.STDOUT,
                               universal_newlines=True )
except Exception as ex:
    print( f"Failed to run '{cmd}'" )
    if hasattr( ex, "message" ):
        print( ex.message )
    elif hasattr( ex, "strerror" ):
        print( ex.strerror)
    else:
        print( ex )

print( cmdRes )

解决方案 11:

我还没有尝试过,但使用 .pyw 文件而不是 .py 文件应该会有所帮助。pyw 文件没有控制台,所以理论上它不应该出现并像后台进程那样工作。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1579  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1355  
  信创产品在政府采购中的占比分析随着信息技术的飞速发展以及国家对信息安全重视程度的不断提高,信创产业应运而生并迅速崛起。信创,即信息技术应用创新,旨在实现信息技术领域的自主可控,减少对国外技术的依赖,保障国家信息安全。政府采购作为推动信创产业发展的重要力量,其对信创产品的采购占比情况备受关注。这不仅关系到信创产业的发展前...
信创和国产化的区别   8  
  信创,即信息技术应用创新产业,旨在实现信息技术领域的自主可控,摆脱对国外技术的依赖。近年来,国货国用信创发展势头迅猛,在诸多领域取得了显著成果。这一发展趋势对科技创新产生了深远的推动作用,不仅提升了我国在信息技术领域的自主创新能力,还为经济社会的数字化转型提供了坚实支撑。信创推动核心技术突破信创产业的发展促使企业和科研...
信创工作   9  
  信创技术,即信息技术应用创新产业,旨在实现信息技术领域的自主可控与安全可靠。近年来,信创技术发展迅猛,对中小企业产生了深远的影响,带来了诸多不可忽视的价值。在数字化转型的浪潮中,中小企业面临着激烈的市场竞争和复杂多变的环境,信创技术的出现为它们提供了新的发展机遇和支撑。信创技术对中小企业的影响技术架构变革信创技术促使中...
信创国产化   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用