子进程

源代码: 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())

另请参见 示例 小节。

创建子进程

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

创建子进程。

limit 参数设置 StreamReader 用于 Process.stdoutProcess.stderr 的缓冲区限制(如果 subprocess.PIPE 传递给 stdoutstderr 参数)。

返回一个 Process 实例。

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

在 3.10 版本中更改: 删除了 loop 参数。

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

运行 cmd shell 命令。

limit 参数设置 StreamReader 用于 Process.stdoutProcess.stderr 的缓冲区限制(如果 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,但有一些显著的差异

此类 不是线程安全的.

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

coroutine wait()

等待子进程终止。

设置并返回 returncode 属性。

注意

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

coroutine 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 不支持子进程。

在 UNIX 上,子进程监视器 用于等待子进程完成,有关更多信息,请参见 进程监视器

在版本 3.8 中更改: UNIX 切换为使用 ThreadedChildWatcher 从不同线程生成子进程,没有任何限制。

使用非活动的当前子进程监视器生成子进程会引发 RuntimeError

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

另请参见

有关 asyncio 中的并发和多线程 部分。

示例

一个使用 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 编写的 相同示例