【问题标题】:How to run a process with timeout and still get stdout at runtime如何运行超时的进程并在运行时仍然获得标准输出
【发布时间】:2016-12-12 13:48:58
【问题描述】:

需求:

  1. X 秒后超时,如果在进程正常结束之前达到超时,则终止进程(以及它打开的所有进程)。
  2. 在运行时读取正在进行的输出。
  3. 使用产生输出的过程、不产生输出的过程以及产生输出的过程,然后停止产生输出(例如,获取 卡住了)。
  4. 在 Windows 上运行。
  5. 在 Python 3.5.2 上运行。

Python 3 子进程模块内置了timeout,我自己也尝试使用计时器和线程实现超时,但它不适用于输出。 readline() 是否阻塞? readlines() 在吐出所有输出之前肯定是在等待进程结束,这不是我需要的(我需要持续)。

我即将切换到 node.js :-(

【问题讨论】:

  • stdout 的问题可能出在子进程中。如果未刷新标准输出缓冲区,则 python 将永远不会接收内容(无论您使用什么语言,这都是相同的)。一种可能的解决方案(未经测试)是在subprocess.Popen 中将孩子的标准输出分配给标准错误。通常 stderr 是无缓冲的。
  • 是的,readline 将阻塞,等待接收下一行,其他任何读取 sys.stdin 的内容也是如此。您可以通过在命令行上指定 -u 选项来告诉 Python 使 sys.stdout 无缓冲。
  • 没有人关心你是否切换到 node.js...

标签: python python-3.x timeout subprocess stdout


【解决方案1】:

我会使用 asyncio 来完成这类任务。

从进程中读取 IO,就像在这个接受的 anwser 中一样: How to stream stdout/stderr from a child process using asyncio, and obtain its exit code after?

(这里不想完全复制)

将其包裹在超时中:

async def killer(trans, timeout):
    await asyncio.sleep(timeout)
    trans.kill()
    print ('killed!!')

trans, *other_stuff = loop.run_until_complete(
                           loop.subprocess_exec(
                                SubprocessProtocol, 'py', '-3', '-c', 'import time; time.sleep(6); print("Yay!")' ,
                                        ) 
                       )

asyncio.ensure_future(killer(trans, 5)) # 5 seconds timeout for the kill
loop.run_forever()

玩得开心……

【讨论】:

  • 很遗憾,在您提到的 5 秒后,我无法让进程超时。
  • @NaphtaliGilead - 进程不会超时,但你的电话会返回,然后你可以杀死它
  • 更新为在 5 秒后终止进程
【解决方案2】:

使用下面的2个python脚本。

  • Master.py 将使用Popen 启动一个新进程,并将启动一个观察线程,该线程将在3.0 秒后终止该进程。

  • 如果写入stdout的数据中没有换行符,slave必须调用flush方法,(在windows上'\n'也会导致flush)。

注意time 模块不是高精度计时器。

在极端情况下,进程的加载时间可能会超过 3.0 秒(从具有 USB 1.0 的闪存驱动器中读取可执行文件)

Master.py

import subprocess, threading, time

def watcher(proc, delay):
    time.sleep(delay)
    proc.kill()

proc = subprocess.Popen('python Slave.py', stdout = subprocess.PIPE)
threading.Thread(target = watcher, args = (proc, 3.0)).start()

data = bytearray()

while proc:
    chunk = proc.stdout.read(1)
    if not chunk:
        break
    data.extend(chunk)
    print(data)

Slave.py

import time, sys

while True:
    time.sleep(0.1)
    sys.stdout.write('aaaa')
    sys.stdout.flush()

【讨论】:

  • 您的示例有效,但我无法运行我的流程并获得输出。
  • 使用可执行文件的完整路径。例如:c:\windows\system32\py.exed:\Slave.py
【解决方案3】:

Python 3.7+ 上,将subprocess.run()capture_output=Truetimeout=<your_timeout> 一起使用。如果命令在<your_timetout> 秒后没有返回,它将终止进程并引发subprocess.TimeoutExpired 异常,该异常将具有.stdout.stderr 属性:

import subprocess

try:
    result = subprocess.run(["sleep", "3"], timeout=2, capture_output=True)
except subprocess.TimeoutExpired as e:
    print("process timed out")
    print(e.stdout)
    print(e.stderr)

您可能还想传递 text=True(或 Python universal_newlines=True),以便 stdoutstderrstrs 而不是 bytes

在旧版本的 Python 上,您需要在调用 subprocess.run() 时将 capture_output=True 替换为 stdout=subprocess.PIPE, stderr=subprocess.PIPE,,其余部分应相同。

编辑:这不是您想要的,因为您需要等待进程终止才能读取输出,但这是我遇到这个问题时想要的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-10-07
    • 1970-01-01
    • 2018-12-06
    • 1970-01-01
    • 2010-10-06
    • 2013-07-25
    • 1970-01-01
    相关资源
    最近更新 更多