Как прочитать первый байт стандартного вывода подпроцесса, а затем отбросить остальные в Python? - PullRequest
30 голосов
/ 31 марта 2011

Я бы хотел прочитать первый байт стандартного вывода подпроцесса, чтобы узнать, что он запущен. После этого я хотел бы отменить все дальнейшие выходные данные, чтобы не беспокоиться о буфере.

Каков наилучший способ сделать это?

Пояснение: Я бы хотел, чтобы подпроцесс продолжал работать вместе с моей программой, я не хочу ждать его завершения или чего-то в этом роде. В идеале был бы простой способ сделать это, не прибегая к threading, fork ing или multiprocessing.

.

Если я игнорирую выходной поток или .close(), он вызывает ошибки, если ему отправляется больше данных, чем может поместиться в его буфере.

Ответы [ 2 ]

68 голосов
/ 17 октября 2012

Если вы используете Python 3.3+, вы можете использовать специальное значение DEVNULL для stdout и stderr, чтобы отменить вывод подпроцесса.

from subprocess import Popen, DEVNULL

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

Или, если вы используете Python2.4+, вы можете смоделировать это с помощью:

import os
from subprocess import Popen

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

Однако это не дает вам возможности прочитать первый байт стандартного вывода.

1 голос
/ 01 апреля 2011

Кажется, это работает, но не выглядит идиоматично.

#!/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)
...