Python:如何写入子进程的标准输入并实时读取其输出
Posted
技术标签:
【中文标题】Python:如何写入子进程的标准输入并实时读取其输出【英文标题】:Python: how to write to stdin of a subprocess and read its output in real time 【发布时间】:2021-02-16 18:58:45 【问题描述】:我有 2 个程序。
第一个(实际上可以用任何语言编写,因此根本无法更改)如下所示:
#!/bin/env python3
import random
while True:
s = input() # get input from stdin
i = random.randint(0, len(s)) # process the input
print(f"New output i", flush=True) # prints processed input to stdout
它永远运行,从stdin
读取一些内容,处理它并将结果写入stdout
。
我正在尝试使用asyncio
库在 Python 中编写第二个程序。
它将第一个程序作为子进程执行,并尝试通过其stdin
为其提供输入,并从其stdout
检索结果。
到目前为止,这是我的代码:
#!/bin/env python3
import asyncio
import asyncio.subprocess as asp
async def get_output(process, input):
out, err = await process.communicate(input)
print(err) # shows that the program crashes
return out
# other attempt to implement
process.stdin.write(input)
await process.stdin.drain() # flush input buffer
out = await process.stdout.read() # program is stuck here
return out
async def create_process(cmd):
process = await asp.create_subprocess_exec(
cmd, stdin=asp.PIPE, stdout=asp.PIPE, stderr=asp.PIPE)
return process
async def run():
process = await create_process("./test.py")
out = await get_output(process, b"input #1")
print(out) # b'New output 4'
out = await get_output(process, b"input #2")
print(out) # b''
out = await get_output(process, b"input #3")
print(out) # b''
out = await get_output(process, b"input #4")
print(out) # b''
async def main():
await asyncio.gather(run())
asyncio.run(main())
我很难实现get_output
功能。它将一个字节串(根据.communicate()
方法的input
参数的需要)作为参数,将其写入程序的stdin
,从其stdout
读取响应并返回它。
现在,只有第一次调用 get_output
才能正常工作。这是因为.communicate()
方法的实现调用了wait()
方法,从而有效地导致程序终止(它不是故意的)。这可以通过检查get_output
函数中err
的值来验证,这表明第一个程序到达EOF
。因此,对get_output
的其他调用返回一个空字节串。
我尝试了另一种方法,甚至不太成功,因为程序卡在out = await process.stdout.read()
行。我还没弄清楚为什么。
我的问题是如何实现get_output
函数以(近)实时捕获程序的输出并保持运行?它不必使用asyncio
,但我发现这个库是迄今为止最好的。
提前谢谢你!
【问题讨论】:
第一个程序是否保证只打印一行输出以响应它已读取的输入行?如果是这样,您可以将await process.stdout.read()
更改为await process.stdout.readline()
,您的第二种方法应该可以工作。 (它现在卡住了,因为它试图在 EOF 之前读取所有内容,并且您的第一个程序永远不会退出,所以 EOF 永远不会发生。)
如果第一个程序不能保证只打印一行输出,那么这个如何实现就不清楚了,因为没有办法知道多少要读取的数据。
@user4815162342 .read(n)
和 .readline()
不起作用。实际上,没有.read*()
方法似乎有效。有没有办法queue 输出?
我想我现在在您的代码中发现了问题 - 请参阅发布的答案。
【参考方案1】:
如果第一个程序保证只打印一行输出以响应它读取的输入行,您可以将await process.stdout.read()
更改为await process.stdout.readline()
,您的第二种方法应该可以工作。
它对您不起作用的原因是您的 run
函数有一个错误:它从不向子进程发送换行符。因此,子进程卡在input()
中并且永远不会响应。如果您在传递给get_output
的字节文字末尾添加\n
,则代码可以正常工作。
【讨论】:
天哪,我怎么会错过这样的事情?谢谢!你知道我如何使用.read()
或等效的方法来读取尽可能多的字节,直到下一个提示?
@Haltarys 没有办法做到这一点。 read()
一直读取到 EOF,对于 read(n)
或 readexactly(n)
(read(n)
可以返回少于 n 个字节),您需要知道 n。您可以发明一个协议,其中子程序以某种方式宣布它将写入多少字节,或使用自定界编码等,但这些将需要修改它,您说这是禁止的。最佳方法将取决于精确的约束。以上是关于Python:如何写入子进程的标准输入并实时读取其输出的主要内容,如果未能解决你的问题,请参考以下文章