在Python中逐行读取stdin输入

8

我正在尝试通过发送一系列命令并捕获输出来测试特定的聊天程序。

我该如何传递:

sleep 2
echo test
sleep 2
echo test1

我尝试过以下方法:
(sleep 2; echo test; sleep 2; echo test1) | python3 test.py

但是它只打印了第一个部分,而对于第二个部分,我什么也没有得到。相反,它进入了一个无限循环。
Python程序代码如下:
import sys, select

while True:
  socket_list = [sys.stdin]
  read_sockets, write_sockets, error_sockets = select.select(socket_list, [], [])
  for sock in read_sockets:
    message = sys.stdin.readline()
    sys.stdout.write("> %s: ")
    sys.stdout.flush()

我应该提到,这不是完整的程序,而是帮助重新创建完全相同效果的部分。


瞎猜一下,不过考虑一下{ sleep 2; echo test; sleep 2; echo test1 },并且将其重定向到Python而不是管道中? - jesse_b
date +%s; (sleep 2; echo test; sleep 2; echo test1) | perl -pe '$_=time()." ".$_' 对我来说显示了两个 test 字符串和两秒的延迟。 - thrig
1
你需要向我们展示你是如何发送和接收的。这听起来像是一个SO的问题,但很难确定。 - Jeff Schaller
1
请问您能否发布预期的输出结果? - fragwürdig
1
假设这是需要多个套接字的示例,使用Twisted(或类似的库)。不要自己实现整个事件分派系统。 - o11c
显示剩余3条评论
2个回答

8
你的例子进入了循环,因为在 echo test1 后,stdin 被关闭,read() 总是返回空。此外,你不应该使用另一个阻塞调用来处理 select。如果你在多个对象上使用它,仍然可能会被先前事件中的 readline 阻塞。
stdin 逐行读取时,你不需要使用 select.select()。最简单的方法可能是:
for line in sys.stdin:
    print(line)

如果你想要将stdin或文件列表作为参数传递给你的程序,Python的fileinput模块是一个开箱即用的解决方案。
如果你仍然想/需要使用select.select()
import os, sys, select

buffer = ""
while True:
  select.select([sys.stdin.fileno()], [], [])
  read = os.read(sys.stdin.fileno(), 512)

  # empty read: EOF
  if len(read) == 0:
    # buffer might not be empty
    if len(buffer) > 0:
      sys.stdout.write(buffer + "\n")

    break

  # find newlines
  parts = read.split("\n")
  buffer += parts.pop(0)

  while len(parts) > 0:
    sys.stdout.write(buffer + "\n")
    buffer = parts.pop(0)

当对象变为就绪状态时,select.select 将解除阻塞。在这种情况下,唯一的对象是 stdin。如果有多个对象,则需要检查 select.select 的返回值以找出哪个对象已准备就绪。使用 os.read() 进行非阻塞读取 stdin(或者可以使用 stdin.read(1) 逐个字符读取)。使用 read.split("\n") 查找换行符,注意单次读取可能会产生多行。

非常好的回复,感谢您发送使用select.select的示例。确实,在最后一个echo之后,有多个b''发送到stdin,因此检查空读取并退出解决了问题。 - Michael

1
在运行catstrace之后,我创建了这个:

import sys

while True:
    data = sys.stdin.read()
    if not len(data):
        break
    sys.stdout.write(data)

命令

(sleep 2; echo test; sleep 2; echo test1) | python3 test.py

输出

test
test1

这个解决方案依赖于以下事实:

如果已经到达文件末尾,f.read() 将返回一个空字符串 ('')。

请参见文件对象的方法
如果你需要使用 select 版本:
import os
import select
import sys

while True:
    rlist = [sys.stdin.fileno()]
    wlist = []
    xlist = []
    rlist, wlist, xlist = select.select(rlist, wlist, xlist)
    if sys.stdin.fileno() in rlist:
        data = os.read(sys.stdin.fileno(), 4096)
        if not len(data):
            break
        sys.stdout.write(data)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接