Python调用readline后subprocess无法读取剩余文件描述符内容咨询
问题原因说明
这个行为属于Python的预期行为,核心逻辑和C标准库的标准IO缓冲机制完全一致:
- Python内置的文件对象默认自带用户态缓冲区,你调用
readline()方法时,Python并不会仅读取恰好一行的字节,而是会一次性读取4KB/8KB(具体大小取决于系统配置)的数据存入进程内部的缓冲区,再从缓冲区里拆分出第一行返回给调用方。此时底层文件描述符的偏移已经被移动到了这整块读取的数据末尾,剩下未返回的行都存在Python进程的私有缓冲区里,内核层面的文件描述符已经没有这些数据了。 subprocess.Popen传递stdin=sys.stdin参数时,实际传递的是sys.stdin对应的底层内核文件描述符,子进程只能读取到该文件描述符当前偏移之后的内容,完全访问不到父进程Python内部缓冲区里剩下的数据,所以就出现了你观察到的“读不到剩余行”的现象。
修复方案
两种最简单的修复方式如下:
方案1:直接读取底层文件描述符跳过Python缓冲
直接调用os.read读取标准输入的第一行,不走Python的文件对象缓冲,保证内核文件偏移只移动到第一行末尾:
#!/usr/bin/env python3 import sys import os from subprocess import Popen from contextlib import suppress def main() -> None: # 直接读底层fd的第一行,不走Python缓冲 header = b'' while b'\n' not in header: chunk = os.read(sys.stdin.fileno(), 1) if not chunk: break header += chunk sys.stdout.buffer.write(header) with suppress(EOFError, KeyboardInterrupt): Popen(['sort', *sys.argv[1:]], stdin=sys.stdin, stdout=sys.stdout) if __name__ == '__main__': main()
方案2:将剩余缓冲内容全部喂给子进程
先把Python缓冲里剩下的内容全部读出来,通过管道传给sort子进程,不用直接传递stdin文件描述符:
#!/usr/bin/env python3 import sys from subprocess import Popen, PIPE from contextlib import suppress def main() -> None: header = sys.stdin.readline() print(header, end='') rest = sys.stdin.read() with suppress(EOFError, KeyboardInterrupt): p = Popen(['sort', *sys.argv[1:]], stdin=PIPE, stdout=sys.stdout) p.communicate(input=rest.encode() if isinstance(rest, str) else rest) if __name__ == '__main__': main()
内容的提问来源于stack exchange,提问作者Patrick Gray
相关产品推荐
相关产品推荐

