【问题标题】:Why stdin.readline doesn't read until the end为什么 stdin.readline 直到最后才读取
【发布时间】:2019-09-23 12:51:20
【问题描述】:

我是 python 新手,我尝试从其他 python 脚本向一个子进程发送信息,但子进程在主进程停止发送之前不会读取任何内容。

我尝试发送以“\n”结尾的新行和行。 我知道我的子进程在流完成之前被阻塞,但是如果我发送 \n 或直接发送 stdin.write('\n'),它应该可以正确读取,但它没有

主要流程:

import subprocess
import time

child = subprocess.Popen("python3 example.py", shell=True, stdin=subprocess.PIPE, universal_newlines=True)
s = "this is the message"
print("MAIN:The child pid is: " + str(child.pid))
for i in range(0, 5):
    print("MAIN:iteration send:" + str(i))
    msg = s + "-" + str(i) + "\n"
    child.stdin.writelines(msg)
    time.sleep(1)
child.kill()

子流程:

import time
from sys import stdin

while True:
    try:
        print("CHILD:before read")
        s = stdin.readline()
        print("CHILD:after read")
        print("CHILD:message received is:" + s)
    except EOFError as err:
        print("M_READ_ERROR")
    time.sleep(1)

我的输出是这样的

MAIN:The child pid is: 18041
MAIN:iteration send:0
CHILD:before read
MAIN:iteration send:1
MAIN:iteration send:2
MAIN:iteration send:3
MAIN:iteration send:4
CHILD:after read
CHILD:message received id:this is the message-0

但我期待的是这样的:

MAIN:The child pid is: 18041
MAIN:iteration send:0
CHILD:before read
CHILD:after read
CHILD:message received id:this is the message-0
MAIN:iteration send:1
CHILD:before read
CHILD:after read
CHILD:message received id:this is the message-1
MAIN:iteration send:2
CHILD:before read
CHILD:after read
CHILD:message received id:this is the message-2
MAIN:iteration send:3
CHILD:before read
CHILD:after read
CHILD:message received id:this is the message-3
MAIN:iteration send:4
CHILD:before read
CHILD:after read
CHILD:message received id:this is the message-4

【问题讨论】:

  • 我在您的代码中没有看到 stdin.readline()。您只有 stdin.read()readline() 的工作方式不同
  • @furas 我都尝试了,但我上传的版本是我最后一次尝试。已编辑
  • 使用child.stdin.flush()

标签: python python-3.x subprocess stdin


【解决方案1】:

您的管道正在使用系统的默认缓冲区大小 (io.DEFAULT_BUFFER_SIZE)。由于缓冲区尚未填满,读取器被阻塞,因此流上没有指示读取可用。

要解决此问题,您需要控制缓冲。有两种方法。

首先,您可以在每次写入后进行显式刷新。

child.stdin.writelines(msg)
child.stdin.flush()

这有效地实现了行缓冲,但在您自己的代码中。

其次,您可以通过传递bufsize kwarg 在Popen() 调用中选择缓冲模式。大于 1 的正 bufsize 设置该值的缓冲区大小,这意味着您的阅读器将在该缓冲区被填充时每隔一段时间收到准备好的信号。但也有特殊情况:

  • 0 表示无缓冲,这样每次写入都会立即刷新;
  • 1 表示 line-buffered,这是一种特殊情况,在用户空间中,io 库会扫描写入的换行符并在它们之后刷新。

您可以传递bufsize=1 以在换行符后强制刷新。在 python3 中,这取决于universal_newlines=True,但你已经有了。

【讨论】:

  • 这很好用,你的解释很清楚。谢谢
猜你喜欢
  • 1970-01-01
  • 2021-03-18
  • 1970-01-01
  • 2011-06-03
  • 1970-01-01
  • 2013-06-16
  • 1970-01-01
  • 2014-03-01
  • 2013-02-21
相关资源
最近更新 更多