如何用subprocess模块监控无换行持续输出的Python脚本
实时监控Python脚本的无换行更新输出
问题场景
需要并行运行并实时监控一个Python脚本的输出,但该脚本通过\r(回车符)更新当前行内容,没有输出换行符,常规的subprocess读取方法(如readline()、read()、communicate())均无法正常获取实时输出。
目标脚本(noNewLine1.py)
import time i=0 print("Starting") while True: time.sleep(1) print("Running {: 5d}".format(i), end="\r") i+=1 print()
在Shell中运行时,第二行会每秒更新一次数字,不会产生新行。
无效的初始尝试
import subprocess import time a = subprocess.Popen(["python3", "noNewLine1.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) while True : time.sleep(1) # b = a.stdout.readline() # 挂起,因为没有换行符触发行读取 # b = a.stdout.read(1) # 同样挂起,因为stdout默认缓冲未刷新 b = a.communicate(timeout=1) # 超时,无法获取实时输出 print(b)
解决方案
方法1:禁用目标脚本的输出缓冲
Python默认会对stdout进行缓冲,当输出没有换行符时,内容会留在缓冲区中,不会发送到管道。可以通过两种方式禁用缓冲:
方式A:修改目标脚本的print语句
在print时添加flush=True强制刷新缓冲区:
import time i=0 print("Starting") while True: time.sleep(1) print("Running {: 5d}".format(i), end="\r", flush=True) i+=1 print()
方式B:启动Python时使用-u参数
无需修改目标脚本,在启动子进程时添加-u参数(禁用缓冲):
import subprocess import time # 使用-u参数禁用目标脚本的stdout缓冲 a = subprocess.Popen( ["python3", "-u", "noNewLine1.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True # 直接读取字符串而非字节流 ) while True: # 读取输出,此时即使没有换行也能获取到内容 chunk = a.stdout.readline() if chunk: # 处理回车符,避免输出混乱 print(chunk.strip('\r'), end='\r') # 检查子进程是否已终止 if a.poll() is not None: break time.sleep(0.1)
方法2:模拟终端环境(无需修改目标脚本)
如果无法修改目标脚本,可以用pty模块模拟终端,因为Python在终端环境下会使用行缓冲而非全缓冲,输出会实时刷新:
import pty import os import subprocess # 创建伪终端 master, slave = pty.openpty() # 将子进程的输出绑定到伪终端的slave端 a = subprocess.Popen( ["python3", "noNewLine1.py"], stdout=slave, stderr=slave, text=True ) os.close(slave) while True: try: # 从主端读取输出 output = os.read(master, 1024) if not output: break # 将回车符替换为换行,方便查看历史输出 print(output.replace('\r', '\n'), end='') except OSError: # 子进程终止后会触发OSError,退出循环 break a.wait()
内容的提问来源于stack exchange,提问作者argentum2f
相关产品推荐
相关产品推荐

