如何在Python中读取子进程stdout的第一个字节并丢弃其余部分?
我想读取一个子程序的标准输出(stdout)中的第一个字节,以确认它已经开始运行。之后,我想丢弃所有后续的输出,这样我就不用担心缓冲区的问题了。
有什么好的方法可以做到这一点呢?
补充说明:我希望这个子程序能和我的程序同时运行,我不想等它结束或其他类似的事情。理想情况下,我希望有一种简单的方法来实现这一点,而不需要使用 threading
、fork
或 multiprocessing
。
如果我忽略输出流,或者用 .close()
关闭它,当发送的数据超过它的缓冲区容量时,就会出现错误。
2 个回答
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)
67
如果你在使用Python 3.3或者更高版本,你可以用一个叫DEVNULL
的特殊值来处理stdout
和stderr
,这样就可以把子进程的输出丢掉,不让它显示出来。
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)
不过这样做的话,你就不能读取stdout
的第一个字节了。