如何从标准输入读取一行后将剩余内容传递给子进程?
解决Python读取stdin首行后子进程无法获取剩余输入的问题
问题现象
当Python通过sys.stdin.buffer.readline()读取标准输入首行后,启动的子进程无法读取剩余的标准输入内容。示例代码及运行结果如下:
import subprocess import sys header = sys.stdin.buffer.readline() print(header) subprocess.run(['nl'], check=True)
运行后子进程无输出:
bash$ printf '%s\n' foo bar baz | python demo1.py b'foo\n'
但移除readline()相关代码后,子进程能正常读取所有输入:
bash$ printf '%s\n' foo bar baz | python -c 'import subprocess; subprocess.run(["nl"], check=True)' 1 foo 2 bar 3 baz
问题原因
Python的标准输入默认使用块缓冲机制,当调用readline()时,操作系统会一次性读取一块数据(通常为4096字节)到Python的内部缓冲区中。剩余的输入内容会留在Python的缓冲区里,而子进程直接从操作系统的标准输入文件描述符读取数据,自然无法获取已经被Python缓冲的内容。这也解释了传入大量数据时,子进程能获取部分内容(未被Python缓冲的部分)的现象。
解决方案
方案1:手动读取剩余内容并传递给子进程
先将Python缓冲区中剩余的所有输入内容读取出来,再通过subprocess.run()的input参数传递给子进程:
import subprocess import sys header = sys.stdin.buffer.readline() print(header) # 读取剩余所有输入内容 remaining_input = sys.stdin.buffer.read() # 将剩余内容作为子进程的输入 subprocess.run(['nl'], input=remaining_input, check=True)
运行验证:
bash$ printf '%s\n' foo bar baz | python demo1.py b'foo\n' 1 bar 2 baz
方案2:禁用Python的stdin缓冲
通过调整文件描述符的属性,禁用Python对标准输入的缓冲,让readline()仅读取实际的一行数据,不预读更多内容(适用于Unix-like系统):
import subprocess import sys import os import fcntl # 获取当前stdin的文件状态标志 flags = fcntl.fcntl(sys.stdin.fileno(), fcntl.F_GETFL) # 设置为无缓冲模式 fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, flags | os.O_DIRECT) header = sys.stdin.buffer.readline() print(header) subprocess.run(['nl'], check=True)
这种方法下,子进程可以直接从操作系统读取剩余的标准输入内容,无需手动传递。
方案3:使用原始文件描述符操作
直接操作stdin的原始文件描述符,绕开Python的缓冲层:
import subprocess import sys # 直接使用raw IO,绕过Python缓冲 raw_stdin = sys.stdin.buffer.raw header = raw_stdin.readline() print(header) subprocess.run(['nl'], check=True)
sys.stdin.buffer.raw是未经Python缓冲的原始IO对象,调用其readline()不会预读数据,剩余内容会留在操作系统的stdin中,供子进程读取。
内容的提问来源于stack exchange,提问作者tripleee
相关产品推荐
相关产品推荐

