子进程

源代码: Lib/asyncio/subprocess.py, Lib/asyncio/base_subprocess.py


本节描述了高级的 async/await asyncio API,用于创建和管理子进程。

以下是一个 asyncio 如何运行 shell 命令并获取其结果的示例

import asyncio

async def run(cmd):
    proc = await asyncio.create_subprocess_shell(
        cmd,
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE)

    stdout, stderr = await proc.communicate()

    print(f'[{cmd!r} exited with {proc.returncode}]')
    if stdout:
        print(f'[stdout]\n{stdout.decode()}')
    if stderr:
        print(f'[stderr]\n{stderr.decode()}')

asyncio.run(run('ls /zzz'))

将打印

['ls /zzz' exited with 1]
[stderr]
ls: /zzz: No such file or directory

由于所有 asyncio 子进程函数都是异步的,并且 asyncio 提供了许多与此类函数配合使用的工具,因此并行执行和监控多个子进程非常容易。修改上述示例以同时运行多个命令确实很简单

async def main():
    await asyncio.gather(
        run('ls /zzz'),
        run('sleep 1; echo "hello"'))

asyncio.run(main())

另请参阅 示例 小节。

创建子进程

async asyncio.create_subprocess_exec(program, *args, stdin=None, stdout=None, stderr=None, limit=None, **kwds)

创建一个子进程。

limit 参数设置 StreamReader 包装器在 stdoutstderr 处的缓冲区限制(如果 subprocess.PIPE 传递给 stdoutstderr 参数)。

返回 Process 实例。

有关其他参数,请参阅 loop.subprocess_exec() 的文档。

版本 3.10 中已更改: 移除了 loop 参数。

async asyncio.create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, limit=None, **kwds)

运行 cmd shell 命令。

limit 参数设置 StreamReader 包装器在 stdoutstderr 处的缓冲区限制(如果 subprocess.PIPE 传递给 stdoutstderr 参数)。

返回 Process 实例。

有关其他参数,请参阅 loop.subprocess_shell() 的文档。

重要

应用程序有责任确保所有空格和特殊字符都经过适当引用,以避免 shell 注入 漏洞。shlex.quote() 函数可用于正确转义用于构造 shell 命令的字符串中的空格和特殊 shell 字符。

版本 3.10 中已更改: 移除了 loop 参数。

备注

如果使用 ProactorEventLoop,Windows 上也支持子进程。有关详细信息,请参阅 Windows 上的子进程支持

参见

asyncio 还具有以下用于处理子进程的 低级 API:loop.subprocess_exec()loop.subprocess_shell()loop.connect_read_pipe()loop.connect_write_pipe(),以及 子进程传输子进程协议

常量

asyncio.subprocess.PIPE

可传递给 stdinstdoutstderr 参数。

如果 PIPE 传递给 stdin 参数,Process.stdin 属性将指向 StreamWriter 实例。

如果 PIPE 传递给 stdoutstderr 参数,Process.stdoutProcess.stderr 属性将指向 StreamReader 实例。

asyncio.subprocess.STDOUT

特殊值,可用作 stderr 参数,表示标准错误应重定向到标准输出。

asyncio.subprocess.DEVNULL

特殊值,可用作进程创建函数中的 stdinstdoutstderr 参数。它表示特殊文件 os.devnull 将用于相应的子进程流。

与子进程交互

create_subprocess_exec()create_subprocess_shell() 函数都返回 Process 类的实例。Process 是一个高级包装器,允许与子进程通信并监视它们的完成。

class asyncio.subprocess.Process

一个对象,它包装由 create_subprocess_exec()create_subprocess_shell() 函数创建的操作系统进程。

此类的设计旨在提供与 subprocess.Popen 类相似的 API,但存在一些显著差异

此类 不是线程安全的

另请参阅 子进程和线程 部分。

async wait()

等待子进程终止。

设置并返回 returncode 属性。

备注

当使用 stdout=PIPEstderr=PIPE 且子进程生成过多输出以至于在等待操作系统管道缓冲区接受更多数据时阻塞时,此方法可能会死锁。使用管道时,请使用 communicate() 方法以避免此情况。

async communicate(input=None)

与进程交互

  1. 将数据发送到 stdin(如果 input 不为 None);

  2. 关闭 stdin

  3. stdoutstderr 读取数据,直到达到 EOF;

  4. 等待进程终止。

可选的 input 参数是将发送到子进程的数据(bytes 对象)。

返回元组 (stdout_data, stderr_data)

如果在将 input 写入 stdin 时引发 BrokenPipeErrorConnectionResetError 异常,则该异常将被忽略。当进程在所有数据写入 stdin 之前退出时,会发生这种情况。

如果希望向进程的 stdin 发送数据,则需要使用 stdin=PIPE 创建进程。同样,要在结果元组中获取除 None 之外的任何内容,必须使用 stdout=PIPE 和/或 stderr=PIPE 参数创建进程。

请注意,读取的数据在内存中缓冲,因此如果数据大小较大或不受限制,请勿使用此方法。

3.12 版本中的变化: input=None 时,stdin 也会关闭。

send_signal(signal)

向子进程发送信号 signal

备注

在 Windows 上,SIGTERMterminate() 的别名。CTRL_C_EVENTCTRL_BREAK_EVENT 可以发送到通过包含 CREATE_NEW_PROCESS_GROUPcreationflags 参数启动的进程。

terminate()

停止子进程。

在 POSIX 系统上,此方法向子进程发送 SIGTERM

在 Windows 上,调用 Win32 API 函数 TerminateProcess() 来停止子进程。

kill()

终止子进程。

在 POSIX 系统上,此方法向子进程发送 SIGKILL

在 Windows 上,此方法是 terminate() 的别名。

stdin

标准输入流(StreamWriter)或 None(如果进程是用 stdin=None 创建的)。

stdout

标准输出流(StreamReader)或 None(如果进程是用 stdout=None 创建的)。

stderr

标准错误流(StreamReader)或 None(如果进程是用 stderr=None 创建的)。

警告

使用 communicate() 方法而不是 process.stdin.write()await process.stdout.read()await process.stderr.read()。这避免了由于流暂停读取或写入并阻塞子进程而导致的死锁。

pid

进程识别号 (PID)。

请注意,对于由 create_subprocess_shell() 函数创建的进程,此属性是生成 shell 的 PID。

returncode

进程退出时的返回代码。

值为 None 表示进程尚未终止。

负值 -N 表示子进程被信号 N 终止(仅限 POSIX)。

子进程和线程

默认情况下,标准的 asyncio 事件循环支持从不同线程运行子进程。

在 Windows 上,子进程仅由 ProactorEventLoop 提供(默认),SelectorEventLoop 不支持子进程。

请注意,替代事件循环实现可能存在自己的限制;请参阅其文档。

示例

一个使用 Process 类控制子进程,并使用 StreamReader 类从其标准输出读取的示例。

子进程由 create_subprocess_exec() 函数创建

import asyncio
import sys

async def get_date():
    code = 'import datetime; print(datetime.datetime.now())'

    # Create the subprocess; redirect the standard output
    # into a pipe.
    proc = await asyncio.create_subprocess_exec(
        sys.executable, '-c', code,
        stdout=asyncio.subprocess.PIPE)

    # Read one line of output.
    data = await proc.stdout.readline()
    line = data.decode('ascii').rstrip()

    # Wait for the subprocess exit.
    await proc.wait()
    return line

date = asyncio.run(get_date())
print(f"Current date: {date}")

另请参阅使用低级 API 编写的 相同示例