要将子进程的stdout保存到变量中以进行进一步处理,并在子进程到达时将其显示出来,以显示该变量:
#!/usr/bin/env python3from io import StringIOfrom subprocess import Popen, PIPEwith Popen('/path/to/script', stdout=PIPE, bufsize=1,universal_newlines=True) as p, StringIO() as buf: for line in p.stdout: print(line, end='') buf.write(line) output = buf.getvalue()rc = p.returnpre保存子进程的stdout和stderr都比较复杂,因为您应该同时使用两个流以避免死锁:
stdout_buf, stderr_buf = StringIO(), StringIO()rc = teed_call('/path/to/script', stdout=stdout_buf, stderr=stderr_buf, universal_newlines=True)output = stdout_buf.getvalue()...在这里
teed_call()定义。
更新: 这是一个更简单的
asyncio版本。
要在单个线程中逐行同时捕获和显示子进程中的stdout和stderr,可以使用异步I / O:
#!/usr/bin/env python3import asyncioimport osimport sysfrom asyncio.subprocess import PIPE@asyncio.coroutinedef read_stream_and_display(stream, display): """Read from stream line by line until EOF, display, and capture the lines. """ output = [] while True: line = yield from stream.readline() if not line: break output.append(line) display(line) # assume it doesn't block return b''.join(output)@asyncio.coroutinedef read_and_display(*cmd): """Capture cmd's stdout, stderr while displaying them as they arrive (line by line). """ # start process process = yield from asyncio.create_subprocess_exec(*cmd, stdout=PIPE, stderr=PIPE) # read child's stdout/stderr concurrently (capture and display) try: stdout, stderr = yield from asyncio.gather( read_stream_and_display(process.stdout, sys.stdout.buffer.write), read_stream_and_display(process.stderr, sys.stderr.buffer.write)) except Exception: process.kill() raise finally: # wait for the process to exit rc = yield from process.wait() return rc, stdout, stderr# run the event loopif os.name == 'nt': loop = asyncio.ProactorEventLoop() # for subprocess' pipes on Windows asyncio.set_event_loop(loop)else: loop = asyncio.get_event_loop()rc, *output = loop.run_until_complete(read_and_display(*cmd))loop.close()
旧版本:
这是一个基于以下
child_process.py示例
tulip的单线程解决方案:
import asyncioimport sysfrom asyncio.subprocess import PIPE@asyncio.coroutinedef read_and_display(*cmd): """Read cmd's stdout, stderr while displaying them as they arrive.""" # start process process = yield from asyncio.create_subprocess_exec(*cmd, stdout=PIPE, stderr=PIPE) # read child's stdout/stderr concurrently stdout, stderr = [], [] # stderr, stdout buffers tasks = { asyncio.Task(process.stdout.readline()): ( stdout, process.stdout, sys.stdout.buffer), asyncio.Task(process.stderr.readline()): ( stderr, process.stderr, sys.stderr.buffer)} while tasks: done, pending = yield from asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED) assert done for future in done: buf, stream, display = tasks.pop(future) line = future.result() if line: # not EOF buf.append(line) # save for later display.write(line) # display in terminal # schedule to read the next line tasks[asyncio.Task(stream.readline())] = buf, stream, display # wait for the process to exit rc = yield from process.wait() return rc, b''.join(stdout), b''.join(stderr)该脚本运行
'/path/to/script命令并逐行同时读取其stdout&stderr。这些行将相应地打印到父级的stdout /
stderr,并另存为字节串以供将来处理。要运行
read_and_display()协程,我们需要一个事件循环:
import osif os.name == 'nt': loop = asyncio.ProactorEventLoop() # for subprocess' pipes on Windows asyncio.set_event_loop(loop)else: loop = asyncio.get_event_loop()try: rc, *output = loop.run_until_complete(read_and_display("/path/to/script")) if rc: sys.exit("child failed with '{}' exit pre".format(rc))finally: loop.close()


