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

Art*_*tem 259 python daemon process

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

Dan*_*Dan 343

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

案例示例:

import subprocess
subprocess.Popen(["rm","-r","some.file"])
Run Code Online (Sandbox Code Playgroud)

这应该rm -r somefile在后台运行. 但要小心:.communicate()如果python脚本中没有任何内容依赖于正在运行的命令的输出,则只在后台运行一个进程:

例如,以下命令不会在后台运行:

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

请参阅此处的文档.

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

  • 好的,那么当你需要Popen()的结果写入stdin时,如何强制进程到后台? (5认同)
  • @Dan `proc = subprocess.Popen(["rm","-r","some.file"])`,然后杀死:`proc.terminate()` (5认同)
  • @Dan:如果进程在后台运行,我该怎么办?我想运行它一段时间(它是我与之交互的守护进程)并在我完成它时将其杀死.文档没有帮助...... (4认同)
  • @JFSebastian:我将其解释为"如何创建一个不会停止执行当前程序的独立进程".您如何建议我编辑它以使其更明确? (2认同)

jkp*_*jkp 75

注意:此答案比2009年发布的答案要短.subprocess现在,在文档中建议使用其他答案中显示的模块

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


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

import os
os.spawnl(os.P_DETACH, 'some_long_running_command')
Run Code Online (Sandbox Code Playgroud)

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

请参阅此处文档.

  • 直接为我崩溃python. (36认同)
  • os.P_DETACH已被os.P_NOWAIT取代. (26认同)
  • 备注:您必须指定可执行文件的完整路径.此函数不会使用PATH变量,并且在Windows下无法使用使用它的变体. (7认同)
  • 人们建议使用`subprocess`能给我们一个提示如何用`subprocess`分离进程吗? (7认同)
  • 如何使用Python脚本(比如attach.py​​)查找后台进程并重定向其IO,以便attach.py​​可以在后台读取/写入some_long_running_prog? (3认同)
  • 从文档:"请注意,子进程模块提供了更强大的工具来生成新进程并检索其结果;使用该模块比使用这些函数更可取" - 请使用下面的子进程答案. (2认同)
  • 在 2021 年,我从上面的代码中得到了“ValueError:spawnv() arg 2不能为空”。 (2认同)

Eli*_*ght 45

您可能想要"如何在Python中调用外部命令"的答案.

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

import os
os.system("some_command &")
Run Code Online (Sandbox Code Playgroud)

基本上,无论你传递给system函数的是什么,都会像在脚本中将它传递给shell一样执行.

  • 恕我直言,python脚本通常被编写为跨平台,如果存在简单的跨平台解决方案,最好坚持下去.永远不知道你将来是否需要与其他平台合作:)或者如果其他人想要将你的脚本迁移到他的平台上. (9认同)
  • 此命令是同步的(即它始终等待已启动进程的终止). (6认同)

f p*_*f p 29

我在这里找到了这个:

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

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

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid
Run Code Online (Sandbox Code Playgroud)

  • +1 用于显示如何保留进程 ID。如果有人想稍后使用进程 ID 终止程序:/sf/ask/1249984991/#17858114 (6认同)
  • 这似乎只适用于 Windows (5认同)

Jim*_* KD 21

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'
Run Code Online (Sandbox Code Playgroud)

  • 该解决方案在Linux上将子进程保留为Zombie。 (3认同)
  • 谢谢@Jimmy,你的答案是唯一适合我的解决方案。 (2认同)

Ger*_*ncy 12

您可能希望开始调查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)
Run Code Online (Sandbox Code Playgroud)

  • `os.fork()`非常有用,但它确实有一个明显的缺点,只能在*nix上使用. (2认同)

Cir*_*四事件 5

捕获输出并在后台运行 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()
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

运行后:

./main.py
Run Code Online (Sandbox Code Playgroud)

标准输出每0.5秒更新一次,每两行包含一次:

0
10
1
11
2
12
3
13
Run Code Online (Sandbox Code Playgroud)

每个日志文件都包含给定进程的相应日志。

灵感来源:https : //eli.thegreenplace.net/2017/interacting-with-a-long-running-child-process-in-python/

已在Ubuntu 18.04,Python 3.6.7上测试。


归档时间:

查看次数:

372562 次

最近记录:

5 年,11 月 前