我能想到几个解决方案。
#1:你可以直接进入源代码communicate
,复制并粘贴它,添加代码来打印每一行,以及缓冲。(如果你自己stdout
的阻塞可能是因为,比如说,一个死锁的父母,你可以使用 athreading.Queue
或其他东西。)这显然有点hacky,但它很容易,而且很安全。
但实际上,communicate
这很复杂,因为它需要完全通用,并处理您不需要的情况。您需要的只是核心技巧:在问题上抛出线程。您只需要一个不会在read
调用之间缓慢或阻塞的专用读取器线程。
像这样的东西:
self.process = subprocess.Popen(self.cmd, stdout=subprocess.PIPE)
lines = []
def reader():
for line in self.process.stdout:
lines.append(line)
sys.stdout.write(line)
t = threading.Thread(target=reader)
t.start()
self.process.wait()
t.join()
reader
您可能需要在线程中进行一些错误处理。而且我不是 100% 确定你可以readline
在这里安全地使用。但这要么有效,要么接近。
#2: 或者你可以创建一个包装类,它接受一个文件对象并在每次有人从它时发送到stdout
/ 。然后手动创建管道,并传入包裹的管道,而不是使用 automagic 。这与 #1 有完全相同的问题(意味着没有问题,或者如果可以阻止,您需要使用 a或其他东西)。stderr
read
PIPE
Queue
sys.stdout.write
像这样的东西:
class TeeReader(object):
def __init__(self, input_file, tee_file):
self.input_file = input_file
self.tee_file = tee_file
def read(self, size=-1):
ret = self.input_file.read(size)
if ret:
self.tee_file.write(ret)
return ret
换句话说,它包装了一个文件对象(或类似文件的东西),并像一个文件对象一样工作。(当您使用时PIPE
, ,process.stdout
是 Unix 上的真实文件对象,但可能只是在 Windows 上的行为。)您需要委托给的任何其他方法都input_file
可以直接委托,而无需任何额外的包装。要么试试这个,看看什么方法communicate
会被AttributeException
寻找并明确编码那些,或者做通常的__getattr__
技巧来委托一切。PS,如果您担心这个“文件对象”的概念意味着磁盘存储,请阅读Wikipedia 上的 Everything is a file。
#3:最后,您可以获取 PyPI 上的“异步子进程”模块之一,或者包含在twisted
其他异步框架中并使用它。(这可以避免死锁问题,但不能保证——您仍然必须确保正确地服务管道。)