python - 如何在不失真的情况下打印和显示子进程标准输出和标准错误输出?

标签 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)

虽然这有几个问题。因为 read() 读取到 EOF,所以 while 循环的第一行将不会返回,直到子进程关闭管道。

我可以用 read(int) 替换 read() 但打印输出失真,在读取字符的末尾被截断。我可以用 readline() 作为替代品,但是打印的输出会因交替的输出行和错误而失真,因为这两种情况同时发生。

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

也许最好按照 answer to another post 中的建议实现 sys.stdout 包装器?不过,我只想在此函数中使用包装器。

社区还有其他想法吗?

感谢您的帮助! :)

编辑:解决方案确实应该是跨平台的,但如果您有不跨平台的想法,请分享它们以继续集思广益。


对于我的另一个 python 子进程头挠头,看看我在 accounting for subprocess overhead in timing 上的另一个问题.

最佳答案

使用 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 仅适用于类 Unix 平台,包括 Cygwin。

如果您需要它在没有 Cygwin 的情况下在 Windows 上工作,它是可行的,但要困难得多。你必须:

关于python - 如何在不失真的情况下打印和显示子进程标准输出和标准错误输出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7729336/

相关文章:

python - 使用 Twisted python 是否有一种机制可以找到类似于 socket.open 的空闲端口?

algorithm - Python:在多维字典中搜索键

python - 脚本不会切换到另一个用户

python - 在 cmd 中通过 Python 运行工具不等待

python - 将 subprocess.check_output 用于带有 2>/dev/null 的命令

Python subprocess.Popen 使用 git pager

python - 使用 scipy.integrate.odeint 求解 odes 系统(不断变化!)?

python - Pandas 将不同索引级别上的 .groupby().sum() 合并到一个结果数据帧中

python - 将数组从 c 转换为 python ctypes

python - 使用生成的文件名进行子进程调用