Python:读取子进程'stdout而不打印到文件

时间:2015-04-22 14:03:08

标签: python subprocess

我有一个名为BOB.exe的可执行函数,只需短暂暂停就可以向stdout输出大量文本。 BOB也有冻结的习惯所以我编写了一个python监视器函数,它使用子进程模块调用BOB可执行文件,将其定向到一个临时文件并监视临时文件的大小以查看它是否已崩溃。这是我目前的解决方案:

#!/usr/bin/python
from subprocess import Popen
import tempfile, time
def runBOB(argsList):

    # Create a temporary file where BOB stdout will be piped
    BOBout = tempfile.NamedTemporaryFile()
    BOBoutSize = 0

    # Start the subprocess of BOB
    BOBsp = Popen(argsList, stdout=BOBout)

    while True:
        # See if subprocess has finished
        if BOBsp.poll() is not None:
            BOBout.close() # Destroy the temp file
            return 0

        # if the size of the stdout file has increased, BOB.exe is still running
        BOBoutSizeNew = os.path.getsize(BOBout.name)
        if BOBoutSizeNew > BOBoutSize:
            BOBoutSize = BOBoutSizeNew
        else: # if not, kill it
            BOBsp.kill()
            BOBout.close() # Destroy the temp file
            return 1

        # Check every 10 seconds
        time.sleep(10)

然而,这非常慢,我认为写入文件是原因。有没有更有效的方法来执行此操作,例如观看stdout流然后立即将其发送到Null?任何减少数百万次打印的东西都可能会有所帮助。还有另一种方法可以看出exe是否崩溃了吗?我应该注意到我并不关心标准输出,无论如何都会被忽略

感谢您的帮助!

1 个答案:

答案 0 :(得分:2)

您可以使用stdout=subprocess.PIPE告诉subprocess使您能够读取子进程的输出而无需将其存储到文件中。棘手的部分是异步执行,以免BOB.exe冻结时死锁。一个简单的方法是使用辅助线程;虽然Python在线程方面声誉不佳,但这实际上是一个很好的用于线程的用例,而GIL并没有妨碍它。

只需创建一个帮助程序线程,除了从Bob输出对应的文件句柄中读取输出外,什么都不做。辅助线程立即丢弃输出并递增字节计数器。主线程实现了与之前完全相同的逻辑,但使用内存计数器而不是重新检查文件大小。当Bob完成或被主线程杀死时,辅助线程将接收EOF并退出。

以下是未经测试的上述实现:

#!/usr/bin/python
import subprocess
import threading
import time
import os

bytes_read = 0

def readBOB(pipe):
    global bytes_read
    bytes_read = 0
    while True:
        # Wait for some data to arrive. This must use os.read rather
        # than pipe.read(1024) because file.read would block us if less
        # than 1024 bytes of data arrives. (Reading one byte at a time
        # with pipe.read(1) would work, but would be too slow at
        # consuming large amounts of data.)
        s = os.read(pipe.fileno(), 1024)
        if not s:
            return  # EOF
        # we are the only writer, so GIL serves as the lock
        bytes_read += len(s)

def runBOB(argsList):
    # Start the subprocess of BOB
    BOBsp = subprocess.Popen(argsList, stdout=subprocess.PIPE)

    thr = threading.Thread(target=readBOB, args=(BOBsp.stdout,))
    thr.start()
    old_bytes_read = -1

    while True:
        # See if subprocess has finished
        if BOBsp.poll() is not None:
            return 0

        # if the size of the stdout has increased, BOB.exe is still running
        new_bytes_read = bytes_read

        if new_bytes_read > old_bytes_read:
            old_bytes_read = new_bytes_read
        else: # if not, kill it (readBOB will exit automatically)
            BOBsp.kill()
            return 1

        # Check every 10 seconds
        time.sleep(10)