也许在以太中有人可以帮助我解决这个问题。 (我在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()
而不是read(int)
,但打印输出会失真,在读取字符的末尾会被切断。我可以readline()
作为替代品,但是当两者同时出现时,打印输出会因交替的输出线和错误而失真。
也许有一个我不知道的read-until-end-of-buffer()
变体?或者也许可以实施?
也许最好按照answer to another post的建议实施sys.stdout
包装器?但是,我只想在这个函数中使用包装器。
来自社区的任何其他想法?
我很感激帮助! :)
编辑:解决方案确实应该是跨平台的,但是如果你有不同的想法,请分享它们以保持头脑风暴。
对于我的另一个python子流程头部抓取器,请查看accounting for subprocess overhead in timing上的另一个问题。
答案 0 :(得分: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)
请注意,fcntl
仅适用于类似Unix的平台,包括Cygwin。
如果你需要它在没有Cygwin的Windows上工作,它是可行的,但它更加困难。你必须:
SetNamedPipeHandleState
与PIPE_NOWAIT
一起使用,使stdout和stderr管道无阻塞WaitForMultipleObjects
代替select
等待数据可用ReadFile
读取数据答案 1 :(得分:0)
将this answer与this结合使用,以下代码适用于我:
import subprocess, sys
p = subprocess.Popen(args, stderr=sys.stdout.fileno(), stdout=subprocess.PIPE)
for line in iter(p.stdout.readline, ""):
print line,
答案 2 :(得分:0)
当我测试它时,似乎readline()阻塞了。但是我能够使用线程分别访问stdout和stderr。代码示例如下:
import os
import sys
import subprocess
import threading
class printstd(threading.Thread):
def __init__(self, std, printstring):
threading.Thread.__init__(self)
self.std = std
self.printstring = printstring
def run(self):
while True:
line = self.std.readline()
if line != '':
print self.printstring, line.rstrip()
else:
break
pythonfile = os.path.join(os.getcwd(), 'mypythonfile.py')
process = subprocess.Popen([sys.executable,'-u',pythonfile], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print 'Process ID:', process.pid
thread1 = printstd(process.stdout, 'stdout:')
thread2 = printstd(process.stderr, 'stderr:')
thread1.start()
thread2.start()
threads = []
threads.append(thread1)
threads.append(thread2)
for t in threads:
t.join()
但是,我不确定这是否是线程安全的。