使用asyncio(Python 3.4+;)从长时间运行的shell命令异步接收输出?

使用asyncio(Python 3.4+;)从长时间运行的shell命令异步接收输出?,python,subprocess,python-asyncio,Python,Subprocess,Python Asyncio,我试图弄清楚如何简单地以非阻塞的方式启动大量长时间运行的shell命令,并在它们完成时以它们完成的顺序异步处理它们的输出,,即使这与它们开始的顺序不同,也可以使用python 3.4和forward中提供的asyncio python库 我找不到一个这样做的简单例子,即使是在中,它似乎也很低级。正是您想要的。它将返回一个进程实例,您可以在该实例上或与之一起使用。使用协同程序,以异步方式获取shell命令输出,并将协同程序传递给,以按完成顺序获取结果: #!/usr/bin/env python3

我试图弄清楚如何简单地以非阻塞的方式启动大量长时间运行的shell命令,并在它们完成时以它们完成的顺序异步处理它们的输出,,即使这与它们开始的顺序不同,也可以使用python 3.4和forward中提供的asyncio python库

我找不到一个这样做的简单例子,即使是在中,它似乎也很低级。

正是您想要的。它将返回一个
进程
实例,您可以在该实例上或与之一起使用。

使用协同程序,以异步方式获取shell命令输出,并将协同程序传递给,以按完成顺序获取结果:

#!/usr/bin/env python3.5
import asyncio
import sys
from asyncio.subprocess import PIPE, STDOUT

async def get_lines(shell_command):
    p = await asyncio.create_subprocess_shell(shell_command,
            stdin=PIPE, stdout=PIPE, stderr=STDOUT)
    return (await p.communicate())[0].splitlines()

async def main():
    # get commands output concurrently
    coros = [get_lines('"{e}" -c "print({i:d}); import time; time.sleep({i:d})"'
                       .format(i=i, e=sys.executable))
             for i in reversed(range(5))]
    for f in asyncio.as_completed(coros): # print in the order they finish
        print(await f)


if sys.platform.startswith('win'):
    loop = asyncio.ProactorEventLoop() # for subprocess' pipes on Windows
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()

我的处境和你的完全一样。在我的例子中,我在几个repo目录中运行多个
git fetch
命令

在第一次试用中,代码如下所示(并且
cmds
['git','fetch']
):

此函数用于一个repo,调用者为多个repo创建任务,并运行事件
循环来完成这些任务

尽管程序运行且磁盘上的结果正确,但来自不同repo的
fetch
输出是交错的。原因是
wait process.wait()
可以在IO阻塞(文件、网络等)的任何时候将控制权返回给调用方(循环调度程序)

一个简单的改变就可以解决这个问题:

async def run_async(path: str, cmds: List[str]):
    """
    Run `cmds` asynchronously in `path` directory
    """
    process = await asyncio.create_subprocess_exec(
        *cmds, stdout=asyncio.subprocess.PIPE, cwd=path)
    stdout, _ = await process.communicate()
    stdout and print(stdout.decode())
这里的基本原理是重定向
stdout
,使其位于一个位置。就我而言,我只是把它打印出来。如果需要输出,最后可以返回

另外,打印顺序可能与开始顺序不同,这在我的情况下是可以的

源代码是。为了提供一些上下文,该项目是一个用于管理多个git repo的命令行工具,它从任何工作目录委托git命令执行。代码不到200行,应该很容易阅读

async def run_async(path: str, cmds: List[str]):
    """
    Run `cmds` asynchronously in `path` directory
    """
    process = await asyncio.create_subprocess_exec(
        *cmds, stdout=asyncio.subprocess.PIPE, cwd=path)
    stdout, _ = await process.communicate()
    stdout and print(stdout.decode())