如何打印和显示subprocess stdout和stderr输出而不失真?

per*_*den 8 python subprocess

也许在以太中有人可以帮我解决这个问题.(我在SO上已经看到了很多类似的问题,但没有一个涉及标准输出和标准错误或处理与我相似的情况,因此这个新问题.)

我有一个python函数打开一个子进程,等待它完成,然后输出返回代码,以及标准输出和标准错误管道的内容.当进程正在运行时,我还想在填充它们时显示两个管道的输出.我的第一次尝试导致了这样的事情:

process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

stdout = str()
stderr = str()
returnCode = None
while True:
    # collect return code and pipe info
    stdoutPiece = process.stdout.read()
    stdout = stdout + stdoutPiece
    stderrPiece = process.stderr.read()
    stderr = stderr + stderrPiece
    returnCode = process.poll()

    # check for the end of pipes and return code
    if stdoutPiece == '' and stderrPiece == '' and returnCode != None:
        return returnCode, stdout, stderr

    if stdoutPiece != '': print(stdoutPiece)
    if stderrPiece != '': print(stderrPiece)
Run Code Online (Sandbox Code Playgroud)

但是有一些问题.因为read()读取直到a EOF,while循环的第一行将不会返回,直到子进程关闭管道.

我可以替换read()赞成read(int)但打印输出失真,在读取字符的末尾切断.我可以readline()作为替代品,但是当同时出现许多两种情况时,打印输出会因交替的输出线和错误而失真.

也许有一种read-until-end-of-buffer()我不知道的变体?或者也许可以实施?

也许最好按照另一篇文章的答案中的sys.stdout建议实现一个包装器?但是,我只想在这个函数中使用包装器.

来自社区的任何其他想法?

我很感激帮助!:)

编辑:解决方案真的应该是跨平台的,但如果你有想法不是,请分享它们以保持头脑风暴.


对于我的另一个python子进程头部抓取器,请看一下关于计算时序中子进程开销的另一个问题.

Ada*_*eld 10

通过使用使管道无阻塞fcntl.fcntl,并使用select.select等待数据在任一管道中可用.例如:

# Helper function to add the O_NONBLOCK flag to a file descriptor
def make_async(fd):
    fcntl.fcntl(fd, fcntl.F_SETFL, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK)

# Helper function to read some data from a file descriptor, ignoring EAGAIN errors
def read_async(fd):
    try:
        return fd.read()
    except IOError, e:
        if e.errno != errno.EAGAIN:
            raise e
        else:
            return ''

process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
make_async(process.stdout)
make_async(process.stderr)

stdout = str()
stderr = str()
returnCode = None

while True:
    # Wait for data to become available 
    select.select([process.stdout, process.stderr], [], [])

    # Try reading some data from each
    stdoutPiece = read_async(process.stdout)
    stderrPiece = read_async(process.stderr)

    if stdoutPiece:
        print stdoutPiece,
    if stderrPiece:
        print stderrPiece,

    stdout += stdoutPiece
    stderr += stderrPiece
    returnCode = process.poll()

    if returnCode != None:
        return (returnCode, stdout, stderr)
Run Code Online (Sandbox Code Playgroud)

请注意,fcntl仅适用于类似Unix的平台,包括Cygwin.

如果你需要它在没有Cygwin的Windows上工作,它是可行的,但它更加困难.你必须:

  • 令人遗憾的是,windows侧面并不像linux那样受到支持,但我猜这就是野兽的本质......一个巨大的,毛茸茸的,封闭源,非标准的管道野兽. (2认同)