You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

subprocess读取stdout阻塞问题及替代pwntools的实现方案

问题解答

代码文件展示

new.py

print("Gib number")
x = input()
print('numba is ', x)

launcher.py(存在问题的实现)

import subprocess as sp

p = sp.Popen(['python', 'new.py'], stdout=sp.PIPE, stderr=sp.STDOUT, stdin=sp.PIPE, text=True)

while True:
    line = p.stdout.readline()
    print('line is', line)
    p.stdin.write('10')


p.wait()

_launcher.py(使用pwntools的实现)

from pwn import process

p = process(['python', 'new.py'], stderr=process.PTY, stdout=process.PTY)

while True:
    output = p.recv()
    print('line is', output)
    p.sendline(10)  # Sends the answer with a newline


p.wait()

1. 为什么launcher.py无法在第二次循环读取new.py的stdout?

核心问题有三个:

  • 缺少换行符:new.py的input()需要读取到换行符才会返回,而p.stdin.write('10')没有写入换行,导致子进程一直卡在input(),不会执行后续的print,自然没有新的stdout输出。
  • 未刷新缓冲区:Python的stdin管道默认有缓冲,write后没有调用flush(),数据不会立即发送到子进程,子进程持续等待输入,流程卡住。
  • 子进程执行完毕后退出:即使解决前两个问题,new.py执行完所有代码后会直接退出,此时子进程的stdout被关闭,第二次readline()会返回空字符串,循环只会持续打印空内容,不会有新输出。

2. pwntools的recv和sendline采用的机制

  • sendline():
    1. 自动在发送内容末尾添加换行符,匹配input()的读取要求;
    2. 立即刷新输出缓冲区,确保数据即时发送到子进程;
    3. 配合process.PTY(伪终端),让子进程认为自己运行在终端环境中,输出使用行缓冲而非管道默认的块缓冲,保证输出能被及时读取。
  • recv():
    1. 主动读取子进程输出流中的所有可用数据(直到超时或EOF);
    2. 处理伪终端的缓冲逻辑,避免因缓冲导致的读取阻塞或遗漏;
    3. 自动管理子进程的文件描述符,处理输入输出的同步问题。

3. 不使用pwntools实现new.py的启动与通信

有两种可靠的实现方式:

方式一:使用pty模块模拟伪终端(推荐)

通过模拟终端环境解决缓冲问题,让子进程的输出行为和在终端中一致:

import subprocess as sp
import pty
import os

# 创建伪终端主从设备
master, slave = pty.openpty()

# 启动子进程,绑定伪终端的从设备作为输入输出
p = sp.Popen(
    ['python', 'new.py'],
    stdout=slave,
    stderr=slave,
    stdin=slave,
    text=True,
    close_fds=True
)
os.close(slave)  # 关闭不需要的从设备文件描述符

try:
    while True:
        # 读取子进程输出,最多读取1024字节
        output = os.read(master, 1024).decode()
        if not output:  # 子进程退出后,读取到空内容
            break
        print('line is', repr(output))
        
        # 发送带换行的输入,确保input()能识别
        os.write(master, b'10\n')
finally:
    p.wait()
    os.close(master)

方式二:手动处理缓冲与换行

通过设置行缓冲、添加换行、刷新缓冲区实现:

import subprocess as sp

# 设置bufsize=1启用行缓冲
p = sp.Popen(
    ['python', 'new.py'],
    stdout=sp.PIPE,
    stderr=sp.STDOUT,
    stdin=sp.PIPE,
    text=True,
    bufsize=1
)

try:
    # 读取第一行输出
    line = p.stdout.readline()
    if line:
        print('line is', line.strip())
        # 发送带换行的输入并刷新缓冲区
        p.stdin.write('10\n')
        p.stdin.flush()
        
        # 读取第二行输出
        line = p.stdout.readline()
        if line:
            print('line is', line.strip())
finally:
    p.wait()

参考资料

  • Python官方文档:subprocess模块中Popen类的缓冲配置、管道通信说明
  • Python官方文档:pty模块的伪终端创建与使用说明
  • 终端缓冲机制:行缓冲、块缓冲的差异与触发条件

内容的提问来源于stack exchange,提问作者Udeshya D.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 06:00:02