将子进程输出显示到stdout并将其重定向

问题描述 投票:13回答:4

我正在通过Python的子流程模块运行脚本。目前我使用:

p = subprocess.Popen('/path/to/script', stdout=subprocess.PIPE, stderr=subprocess.PIPE)
result = p.communicate()

然后将结果打印到标准输出。一切都很好,但是由于脚本需要很长时间才能完成,因此我也希望将脚本实时输出到stdout。我通过管道传输输出的原因是因为我想解析它。

python subprocess stdout
4个回答
15
投票

将子流程的标准输出保存到变量以进行进一步处理,并保存到display it while the child process is running as it arrives

#!/usr/bin/env python3
from io import StringIO
from subprocess import Popen, PIPE

with 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.returncode

保存子进程的stdout和stderr都比较复杂,因为您应该consume both streams concurrently to avoid a deadlock

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() is define here


更新:这是teed_call()


旧版本:

这里是基于a simpler asyncio version的单线程解决方案:

asyncio

脚本运行child_process.py example from tulip命令并逐行同时读取其stdout&stderr。这些行将相应地打印到父级的stdout / stderr,并另存为字节串以供将来处理。要运行child_process.py协程,我们需要一个事件循环:

tulip

1
投票

import asyncio import sys from asyncio.subprocess import PIPE @asyncio.coroutine def 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

0
投票

Popen.communicate文档明确指出:

read_and_display()

import os if 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 code".format(rc)) finally: loop.close()

因此,如果需要实时输出,则需要使用类似以下的内容:

p.communicate() waits for the subprocess to complete

0
投票

这会将stdout和stderr都打印到终端,并将stdout和stderr都保存到变量中:

p.communicate()

但是,根据您的实际工作,可能需要注意:通过使用p = subprocess.Popen('/path/to/script', stdout=subprocess.PIPE, stderr=subprocess.PIPE) for line in p.stdout: # do something with this individual line print line ,我们无法再区分stdout和stderr,并且通过调用Note: The data read is buffered in memory, so do not use this method if the data size is large or unlimited. ,您的输出将始终打印到stdout,不管它来自stdout还是stderr。

对于Python <3.7,您将需要使用https://docs.python.org/2/library/subprocess.html#subprocess.Popen.communicate而不是stream_p = subprocess.Popen('/path/to/script', stdout=subprocess.PIPE, stderr=subprocess.PIPE) while stream_line in stream_p: #Parse it the way you want print stream_line

版本3.7中的新增功能:已添加文本作为Universal_newlines的易读别名。

来源:from subprocess import Popen, PIPE, STDOUT with Popen(command, stdout=PIPE, stderr=STDOUT, text=True, bufsize=1) as p: output = "".join([print(buf, end="") or buf for buf in p.stdout])

© www.soinside.com 2019 - 2024. All rights reserved.