小编典典

如何在不失真的情况下打印和显示子进程stdout和stderr输出?

python

也许在以太坊里有人可以帮助我。(我在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)

但是有几个问题。因为read()读取直到an EOF,所以while循环的第一行直到子进程关闭管道才返回。

我可以替换为read()read(int)但是打印输出失真,在读取字符的末尾切断。我可以readline()代替它,但是当同时发生很多输出和错误时,打印输出会因输出和错误的交替出现而失真。

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

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

社区还有其他想法吗?

感谢您的帮助!:)

编辑 :解决方案确实应该是跨平台的,但是如果您没有想法,请与他人分享以保持头脑风暴。



阅读 216

收藏
2020-12-20

共1个答案

小编典典

使用来使管道成为非阻塞状态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)

请注意,fcntl仅在包括Cygwin的类Unix平台上可用。

如果您需要它在不使用Cygwin的Windows上运行,那是可行的,但难度要大得多。您必须:

2020-12-20