如何读取子进程标准输出的第一个字节,然后在 Python 中丢弃其余字节?

Posted

技术标签:

【中文标题】如何读取子进程标准输出的第一个字节,然后在 Python 中丢弃其余字节?【英文标题】:How to read the first byte of a subprocess's stdout and then discard the rest in Python? 【发布时间】:2011-07-26 14:08:26 【问题描述】:

我想读取子进程的标准输出的第一个字节,以了解它已经开始运行。之后我想丢弃所有进一步的输出,这样我就不必担心缓冲区了。

最好的方法是什么?

澄清:我希望子进程继续与我的程序一起运行,我不想等待它终止或类似的事情。理想情况下,有一些简单的方法可以做到这一点,而无需求助于threadingforking 或multiprocessing

如果我忽略输出流,或者.close() 它,如果发送的数据多于缓冲区容纳不下的数据,则会导致错误。

【问题讨论】:

我的回答不正是你想要的吗?启动子进程,读取第一个字节,然后继续并行运行进程... 据我所知,它会继续将stdoutstderr 读入缓冲区,浪费内存。我不确定,但是如果缓冲区填满,这也可能导致子进程阻塞。我想避免这种情况。 他的解决方案不会阻塞,因为它从不读取超过第一个字节的任何内容。但你不知道它什么时候完成。 wait 不会读取任何内容,但可能会阻塞(如果程序写入过多并开始等待操作系统读取管道)。我的communicate 不会,它会读取内容,占用内存。但是您不阅读就无法知道该过程何时结束...让我提供另一种解决方案 @Jeremy 我修改了我的示例以证明没有浪费任何内存。使用进程监视器运行并观察进程的内存使用情况。它永远不会上升。 我已经测试过了,第二个进程阻塞一次,第一个进程中的stdout缓冲区已满。 【参考方案1】:

如果您使用的是 Python 3.3+,则可以使用 stdoutstderr 的特殊值 DEVNULL 来丢弃子进程输出。

from subprocess import Popen, DEVNULL

process = Popen(["mycmd", "myarg"], stdout=DEVNULL, stderr=DEVNULL)

或者,如果您使用的是 Python 2.4+,您可以使用以下方法进行模拟:

import os
from subprocess import Popen

DEVNULL = open(os.devnull, 'wb')
process = Popen(["mycmd", "myarg"], stdout=DEVNULL, stderr=DEVNULL)

但是这并没有让您有机会读取标准输出的第一个字节。

【讨论】:

换句话说......它没有回答问题。 它回答了帖子标题中的问题。因此,我认为这对其他在互联网搜索中到达此页面的人会很有用。 这会干扰subprocess.check_call吗?我不这么认为,因为退出状态是由wait 给出的,但是,为了确定。 此技术也适用于 check_call,因为它采用与 Popen 相同的参数。 DEVNULL 对我不起作用。我正在使用 Python 3.2.3【参考方案2】:

这似乎可行,但感觉不习惯。

#!/usr/bin/env python3.1
import threading
import subprocess

def discard_stream_while_running(stream, process):
    while process.poll() is None:
        stream.read(1024)

def discard_subprocess_pipes(process, out=True, err=True, in_=True):
    if out and process.stdout is not None and not process.stdout.closed:
        t = threading.Thread(target=discard_stream_while_running, args=(process.stdout, process))
        t.start()

    if err and process.stderr is not None and not process.stderr.closed:
        u = threading.Thread(target=discard_stream_while_running, args=(process.stderr, process))
        u.start()

    if in_ and process.stdin is not None and not process.stdin.closed:
        process.stdin.close()

示例/测试用法

if __name__ == "__main__":
    import tempfile
    import textwrap
    import time

    with tempfile.NamedTemporaryFile("w+t", prefix="example-", suffix=".py") as f:
        f.write(textwrap.dedent("""
            import sys
            import time

            sys.stderr.write(" byte(s) read through stdin.\\n"
                             .format(len(sys.stdin.read())))

            # Push a couple of MB/s to stdout, messages to stderr.
            while True:
                sys.stdout.write("Hello Parent\\n" * 1000000)
                sys.stderr.write("Subprocess Writing Data\\n")
                time.sleep(0.5)
        """))
        f.flush()

        p = subprocess.Popen(["python3.1", f.name],
                             stdout=subprocess.PIPE,
                             stdin=subprocess.PIPE)

        p.stdin.write("Hello Child\n".encode())

        discard_subprocess_pipes(p) # <-- Here

        for s in range(16, 0, -1):
            print("Main Process Running For", s, "More Seconds")
            time.sleep(1)

【讨论】:

你不是在等待第一个字节被读取。虽然:p.poll() is NonePopen() 之后表示子进程 正在 运行。如果这就是你所需要的,那么你应该use DEVNULL-based solution instead。

以上是关于如何读取子进程标准输出的第一个字节,然后在 Python 中丢弃其余字节?的主要内容,如果未能解决你的问题,请参考以下文章

Python:如何写入子进程的标准输入并实时读取其输出

使用 BOOST 进程在单独的线程中读取子进程标准输出

python:在线程中读取子进程输出

Python 运行守护程序子进程并读取标准输出

Python子进程将数据定向到标准输入

持久的子进程管道 - 没有读取标准输出