子进程

源代码: 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() 函数创建的 OS 进程的对象。

此类旨在具有与 subprocess.Popen 类类似的 API,但有一些显着的差异

此类是非线程安全的。

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

协程 wait()

等待子进程终止。

设置并返回 returncode 属性。

注意

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

协程 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 可以发送到使用 creationflags 参数(包括 CREATE_NEW_PROCESS_GROUP)启动的进程。

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 从不同的线程生成子进程,没有任何限制。

使用 inactive 当前子进程监视器生成子进程会引发 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 编写的相同示例