使用subprocess.Popen运行脚本时stdout/stderr显示BufferedReader问题求助
subprocess.Popen无法获取stdout/stderr输出解决方案
根本原因
subprocess.Popen 是异步非阻塞接口,调用后仅启动子进程,不会等待子进程执行完成,也不会自动读取管道中的输出内容。你代码中直接打印的proc.stdout和proc.stderr是缓冲区IO对象,不是实际输出内容。而subprocess.run是阻塞接口,会等待子进程运行结束后自动收集stdout、stderr的内容,因此可以直接拿到输出。
解决思路
方案1:等待进程结束后读取全部输出
调用proc.wait()方法阻塞等待子进程执行完成,再调用read()读取缓冲区内容,示例代码如下:
proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) print(proc.pid) # 阻塞等待子进程执行完成 proc.wait() # 读取输出并解码,编码可根据你的系统环境调整 stdout = proc.stdout.read().decode('utf-8') stderr = proc.stderr.read().decode('utf-8') print(f'err is: {stderr}') print(f'stdout: {stdout}')
方案2:实时读取子进程输出
如果子进程运行时间较长,不想等待进程结束再拿输出,可以逐行读取缓冲区内容,实现实时输出:
# text=True参数可以直接返回字符串,无需手动解码 proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) print(proc.pid) # 实时读取标准输出 for line in iter(proc.stdout.readline, ''): print(f'stdout: {line.strip()}') # 标准输出读取完成后读取标准错误 for line in iter(proc.stderr.readline, ''): print(f'err: {line.strip()}') # 等待进程退出获取返回码 proc.wait()
方案3:解决子进程输出缓冲问题
部分脚本默认开启标准输出缓冲,仅当缓冲区满或进程退出时才会输出内容到管道,遇到这种情况可以在启动Python脚本时添加-u参数禁用缓冲,修改command如下:
command = ['python3', '-u', PosixPath('myrepo/myscript.py'), '-c', PosixPath('myrepo/config.json')]
线程终止时关闭子进程实现
你可以将Popen实例存为线程的属性,在停止线程触发event时主动终止子进程,修改后的线程类示例如下:
class my_thread(threading.Thread): def __init__(self, event): super(my_thread, self).__init__() self._event = event self.proc = None def run(self): command = ['python3', '-u', PosixPath('myrepo/myscript.py'), '-c', PosixPath('myrepo/config.json')] self.proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) print(self.proc.pid) # 读取输出的同时检测停止事件 while not self._event.is_set(): line = self.proc.stdout.readline() if not line: break print(f'stdout: {line.strip()}') # 停止事件触发后终止仍在运行的子进程 if self.proc.poll() is None: self.proc.terminate() self.proc.wait()
内容的提问来源于stack exchange,提问作者Maryam
相关产品推荐
相关产品推荐

