subprocess.Popen 不能直接用 stdout.read(),因为它会阻塞至子进程结束才返回全部输出,无法实时获取;且若子进程缓冲未刷新(如未设 -u 或 flush=True),父进程可能永久卡住。

subprocess.Popen 为什么不能直接用 stdout.read()?
因为 stdout.read() 是阻塞式读取,会一直等到子进程结束才返回全部输出,完全违背“实时”需求。更糟的是,如果子进程持续输出但不主动刷新缓冲区(比如 Python 脚本没加 print(..., flush=True)),父进程可能永远卡在 read 上。
真正能实时读的,是配合 stdout=PIPE + iter(stdout.readline, b'') 或 stdout.readline() 循环,但前提是子进程输出行缓冲或无缓冲——否则你得先解决子进程自身的缓冲问题。
- Linux/macOS 下可用
stdbuf -oL -eL command强制行缓冲 - Python 子进程可加
-u参数:如python -u script.py - 避免用
shell=True,它会干扰 stdbuf 和 -u 的行为
用 iter(stdout.readline, b'') 实现非阻塞逐行读取
这是最轻量、兼容性最好的方式,适用于大多数命令行工具和脚本。关键点在于:必须设 bufsize=1(行缓冲)或 0(无缓冲),且 stdout 必须是二进制模式(text=False)才能用 b'' 终止符。
import subprocess
<p>proc = subprocess.Popen(
['python', '-u', '-c', 'for i in range(3): print(f"line {i}"); import time; time.sleep(1)'],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
bufsize=1,
text=False # 注意:text=False 才能用 b''
)</p><p>for line in iter(proc.stdout.readline, b''):
print(f"→ {line.decode().strip()}")</p><p>proc.wait()
- 如果子进程提前退出,
readline()会返回空 bytes,循环自然终止 -
bufsize=1在二进制模式下实际启用行缓冲,比bufsize=0更省 CPU - 别漏掉
proc.wait(),否则子进程可能变成僵尸进程
想同时读 stdout 和 stderr 还要实时?用 threading + queue
单线程下无法同时阻塞读两个流而不卡死(一个流没输出时会 block 整个循环)。标准解法是开两个线程分别读,把结果丢进 queue.Queue,主线程消费。
立即学习“Python免费学习笔记(深入)”;
import subprocess, threading, queue
<p>def enqueue_output(out, queue):
for line in iter(out.readline, b''):
queue.put(('stdout', line))
out.close()</p><p>proc = subprocess.Popen(
['python', '-u', '-c', 'print("out"); print("err", file=<strong>import</strong>("sys").stderr);'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
bufsize=1,
text=False
)</p><p>q = queue.Queue()
threading.Thread(target=enqueue_output, args=(proc.stdout, q), daemon=True).start()
threading.Thread(target=enqueue_output, args=(proc.stderr, q), daemon=True).start()</p><p>while True:
try:
stream, line = q.get(timeout=0.1)
print(f"[{stream}] {line.decode().strip()}")
except queue.Empty:
if proc.poll() is not None:
break
- 两个线程都设
daemon=True,避免主程序退出时卡住 -
timeout=0.1避免主线程永久阻塞,配合proc.poll()判断是否结束 - 注意:Windows 下某些命令(如
dir)可能不按行输出,这时需改用read(1)单字节读,但性能差很多
subprocess.run 不适合实时读取,别被文档误导
文档里说 run “等同于 Popen + wait”,但它内部做了完整捕获(stdout=subprocess.PIPE + communicate()),全程阻塞。哪怕你传了 stdout=PIPE,run 也只在返回对象里给你最终结果,没有中间过程。
- 想“实时”,必须用
Popen,绕不开手动管理管道和生命周期 -
communicate()是安全的全量读取,但不是实时;它和readline()是互斥方案 - 别试图在
run返回后去读它的stdout属性——那只是 bytes 缓存,不是活的 pipe 对象
真正麻烦的从来不是语法,而是子进程是否真的在逐行输出、缓冲区有没有被 shell 或 libc 拦截、Windows 控制台对 \r\n 的处理……这些细节一错,实时就变成“等它自己结束”。


















