简体   繁体   English

使用 asyncio 将 bash 作为 Python 的子进程运行,但 bash 提示被延迟

[英]Running bash as a subprocess to Python using asyncio, but bash prompts are delayed

I wish to control a long-running interactive Bash subprocess from Python's asyncio, send it commands one at a time, and receive results back from it.我希望从 Python 的 asyncio 控制一个长时间运行的交互式 Bash 子进程,一次发送一个命令,然后从它接收结果。

The code fragment below works perfectly well in Python 3.7.0, Darwin Kernel Version 16.7.0, except that Bash prompts do not appear immediately on stderr , but appear to "queue up" until something else writes to stderr . The code fragment below works perfectly well in Python 3.7.0, Darwin Kernel Version 16.7.0, except that Bash prompts do not appear immediately on stderr , but appear to "queue up" until something else writes to stderr .

This is a problem because the original program needs to receive the Bash prompt to know that the previous command has finished.这是一个问题,因为原程序需要收到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())

Results:结果:

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'

Note that the three prompts all come out together at the end, and only once an error was deliberately caused.注意最后三个提示都是一起出来的,而且只有一次是故意造成的错误。 The desired behavior would of course be for the prompts to appear immediately as they occurred.期望的行为当然是提示在它们发生时立即出现。

Using proc.stderr.read() instead of proc.stderr.read() results in more code but just the same results.使用proc.stderr.read()而不是proc.stderr.read()会产生更多代码,但结果相同。

I'm a little surprised to see that bash: no job control in this shell message appear in stderr , because I am running bash -i and because $PS1 is set, and I wonder if that has something to do with the issue but haven't been able to take that further.看到bash: no job control in this shell消息出现在stderr中,我有点惊讶,因为我正在运行bash -i ,但我想知道是否有这个问题,因为$PS1 PS19CCE69Z无法更进一步。

This held me up for half a day, but once I finished writing the question up, it took me ten minutes to come up with a workaround.这让我呆了半天,但是当我写完问题后,我花了十分钟才想出一个解决方法。

If I modify the prompt so it ends with a \n , then proc.stderr is in fact flushed, and everything works absolutely perfectly.如果我修改提示使其以\n结尾,那么proc.stderr实际上已被刷新,并且一切都运行得非常完美。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM