我有一个Python
脚本来启动一个进程(CMake
),并将其输出重定向到控制台:
proc = subprocess.Popen([cmake,my_args], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in proc.stdout:
sys.stdout.write(line.decode('utf-8'))
res = proc.wait()
这很好,并且来自CMake
的任何消息都输出到我的控制台。
但是,在某些时候,CMake
脚本使用此代码显示了进度:
execute_process(COMMAND ${CMAKE_COMMAND} -E echo_append "Adding modules")
...
execute_process(COMMAND ${CMAKE_COMMAND} -E echo_append "." )
...
execute_process(COMMAND ${CMAKE_COMMAND} -E echo "done\n" )
这很聪明,导致“正在添加模块....”显示在一行上,并且为每个处理的模块添加了一个新的点。
但是由于此输出在完成之前没有换行符,因此只有在添加最后一个模块并显示Python
后,我的"done\n"
脚本才会报告该输出。我希望我的Python
脚本逐点报告此消息,以便可以查看进度。
是否有任何方法可以捕获子流程输出,无论其是否包含新行?
注意:我可以修改Python
或CMake
脚本...
完整答案:
这是正确的代码:
import subprocess
import sys
proc = subprocess.Popen([cmake,my_args], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
while True:
out = proc.stdout.read(1)
if out == '' and proc.poll() != None:
break
if out != '':
sys.stdout.write(out)
sys.stdout.flush()
proc.stdout.read(1)
确保缓冲区大小为1,并且proc.poll() != None
确保在字符之间等待时输出不会停止。另外,我想您可以使用print(out, '')
代替:
sys.stdout.write(out)
sys.stdout.flush()
但是我没有时间测试:)
复杂且不完整的答案:
这可能有效,但需要修改。这是一种更“低级”的方法。
您需要在bufsize
构造函数中设置subproccess.Popen
参数。默认值为-1,这是系统默认缓冲区,在大多数情况下为“行缓冲”。
因此您需要设置bufsize = 0
才能获取每个字符。在非常繁忙的应用程序中,这可能会导致性能问题(因为读/写系统调用非常昂贵)
根据the docs:
[
bufsize
将在创建open()
管道文件对象时作为stdin/stdout/stderr
函数的相应参数提供:0表示未缓冲(读和写是一个系统调用,并且可以返回短整数)
1表示行缓冲(仅在
universal_newlines=True
,即在文本模式下可用)任何其他正值表示使用大约那个大小的缓冲区
负
bufsize
(默认值)表示将使用系统默认值io.DEFAULT_BUFFER_SIZE
。