也许天底下有人可以帮我解决这个问题。 (我在 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 上工作,它是可行的,但要困难得多。你必须:
- 使用pywin32调用 native Win32 API 的库
- 使用
SetNamedPipeHandleState
使用PIPE_NOWAIT
使 stdout 和 stderr 管道成为非阻塞 - 使用
WaitForMultipleObjects
而不是select
等待数据可用 - 使用
ReadFile
读取数据
关于python - 如何在不失真的情况下打印和显示子进程标准输出和标准错误输出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7729336/