如何使用超时运行进程并在运行时仍然获得stdout

时间:2016-08-06 06:20:45

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

需要:

  1. X秒后超时,如果在进程正常结束之前达到超时,则终止进程(以及它打开的所有进程)。
  2. 在运行时读取正在进行的输出。
  3. 处理产生输出的进程,不产生输出的进程,以及产生输出的进程,然后停止生成输出(例如获取 陷)。
  4. 在Windows上运行。
  5. 在Python 3.5.2上运行。
  6. Python 3子进程模块内置了timeout,我也尝试使用计时器和使用线程来实现超时,但它并不适用于输出。 readline()是否阻止? readlines()肯定会在吐出所有输出之前等待进程结束,这不是我需要的(我需要继续)。

    我接近切换到node.js: - (

3 个答案:

答案 0 :(得分:4)

我会使用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()

玩得开心......

答案 1 :(得分:0)

使用下面的2 python脚本。

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

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

  

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

     

在极端情况下(从具有USB 1.0的闪存驱动器读取可执行文件),进程的加载时间可能超过3.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()

答案 2 :(得分:0)

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 <3.7上传递universal_newlines=True),以使stdoutstderrstr而不是{{ 1}}。

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

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