[我希望从Python的asyncio控制一个长时间运行的交互式Bash子进程,一次发送一个命令,并从中接收结果。
下面的代码片段在Python 3.7.0,Darwin Kernel版本16.7.0和except中都运行良好,Bash提示不会立即出现在stderr
上,而是似乎在“排队”,直到有其他内容写入为止到stderr
。
这是一个问题,因为原始程序需要接收Bash提示才能知道先前的命令已完成。
from asyncio.subprocess import PIPE
import asyncio
async def run():
proc = await asyncio.create_subprocess_exec(
'/bin/bash', '-i', stdin=PIPE, stdout=PIPE, stderr=PIPE
)
async def read(stream):
message = 'E' if stream is proc.stderr else 'O'
while True:
line = await stream.readline()
if line:
print(message, line)
else:
break
async def write():
for command in (b'echo PS1=$PS1', b'ls sub.py', b'ls DOESNT-EXIST'):
proc.stdin.write(command + b'\n')
await proc.stdin.drain()
await asyncio.sleep(0.01) # TODO: need instead to wait for prompt
await asyncio.gather(
read(proc.stderr),
read(proc.stdout),
write(),
)
asyncio.run(run())
结果:
E b'bash: no job control in this shell\n'
O b'PS1=\\u@\\h:\\w$\n'
O b'sub.py\n'
E b'tom@bantam:/code/test/python$ tom@bantam:/code/test/python$ tom@bantam:/code/test/python$ ls: DOESNT-EXIST: No such file or directory\n'
请注意,这三个提示最后都一起出现,并且只有在故意导致错误的情况下才会出现。当然,期望的行为是使提示在出现时立即出现。
使用proc.stderr.read()
而不是proc.stderr.read()
会产生更多的代码,但结果相同。
[我有点惊讶地看到bash: no job control in this shell
消息出现在stderr
中,因为我正在运行bash -i
并且因为设置了$PS1
,我想知道这是否与问题有关,但是避难所无法做到这一点。
这使我忙了半天,但是当我完成问题的写作后,花了十分钟的时间才找到解决方法。
如果我修改提示,使其以\n
结尾,则实际上proc.stderr
已刷新,并且一切工作都非常完美。