子进程
******

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

另请参阅 Examples 小节。


创建子进程
==========

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

   创建一个子进程。

   *limit* 参数为 "stdout" 和 "stderr" 设置 "StreamReader" 包装器的缓
   冲区上限（如果将 "subprocess.PIPE" 传给 *stdout* 和 *stderr* 参数）
   。

   返回一个 "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* 参数为 "stdout" 和 "stderr" 设置 "StreamReader" 包装器的缓
   冲区上限（如果将 "subprocess.PIPE" 传给 *stdout* 和 *stderr* 参数）
   。

   返回一个 "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

   可以被传递给 *stdin*, *stdout* 或 *stderr* 形参。

   如果将 *PIPE* 传给 *stdin* 参数，则 "Process.stdin" 属性将指向一个
   "StreamWriter" 实例。

   如果将 *PIPE* 传给 *stdout* 或 *stderr* 参数，则 "Process.stdout"
   和 "Process.stderr" 属性将指向 "StreamReader" 实例。

asyncio.subprocess.STDOUT

   可以用作 *stderr* 参数的特殊值，表示标准错误应当被重定向到标准输出
   。

asyncio.subprocess.DEVNULL

   可以用作进程创建函数的 *stdin*, *stdout* 或 *stderr* 参数的特殊值。
   它表示将为相应的子进程流使用特殊文件 "os.devnull".


与子进程交互
============

"create_subprocess_exec()" 和 "create_subprocess_shell()" 函数都返回
*Process* 类的实例。 *Process* 是一个高层级包装器，它允许与子进程通信
并监视其完成情况。

class asyncio.subprocess.Process

   一个用于包装由 "create_subprocess_exec()" 和
   "create_subprocess_shell()" 函数创建的 OS 进程的对象。

   这个类被设计为具有与 "subprocess.Popen" 类相似的 API，但两者有一些
   重要的差异：

   * 不同于 Popen，Process 实例没有与 "poll()" 方法等价的方法；

   * "communicate()" 和 "wait()" 方法没有 *timeout* 形参：请使用
     "wait_for()" 函数；

   * "Process.wait()" 方法是异步的，而 "subprocess.Popen.wait()" 方法
     则被实现为阻塞型忙循环；

   * *universal_newlines* 形参不被支持。

   这个类 不是线程安全的。

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

   async wait()

      等待子进程终结。

      设置并返回 "returncode" 属性。

      备注:

        当使用 "stdout=PIPE" 或 "stderr=PIPE" 并且子进程产生了足以阻塞
        OS 管道缓冲区等待接收更多的数据的输出时，此方法会发生死锁。当
        使用管道时请使用 "communicate()" 方法来避免这种情况。

   async communicate(input=None)

      与进程交互：

      1. 发送数据到 *stdin* (如果 *input* 不为 "None")；

      2. 关闭 *stdin*;

      3. 从 *stdout* 和 *stderr* 读取数据，直至到达 EOF；

      4. 等待进程终结。

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

      返回一个元组 "(stdout_data, stderr_data)"。

      如果在将 *input* 写入到 *stdin* 时引发了 "BrokenPipeError" 或
      "ConnectionResetError" 异常，异常会被忽略。此条件会在进程先于所
      有数据被写入到 *stdin* 之前退出时发生。

      如果想要将数据发送到进程的 *stdin*，则创建进程时必须使用
      "stdin=PIPE"。类似地，要在结果元组中获得任何不为 "None" 的值，则
      创建进程时必须使用 "stdout=PIPE" 和/或 "stderr=PIPE" 参数。

      注意，数据读取在内存中是带缓冲的，因此如果数据量过大或不受限则不
      要使用此方法。

      在 3.12 版本发生变更: *stdin* 在 "input=None" 时也会被关闭。

   send_signal(signal)

      将信号 *signal* 发送给子进程。

      备注:

        在 Windows 上，"SIGTERM" 是 "terminate()" 的别名。
        "CTRL_C_EVENT" 和 "CTRL_BREAK_EVENT" 可被发送给启动时带有
        *creationflags* 形参且其中包括 "CREATE_NEW_PROCESS_GROUP" 的进
        程。

   terminate()

      停止子进程。

      在 POSIX 系统上此方法会发送 "SIGTERM" 给子进程。

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

   kill()

      杀掉子进程。

      在 POSIX 系统中此方法会发送 "SIGKILL" 给子进程。

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

   stdin

      标准输入流 ("StreamWriter") 或者如果进程创建时设置了
      "stdin=None" 则为 "None".

   stdout

      标准输出流 ("StreamReader") 或者如果进程创建时设置了
      "stdout=None" 则为 "None".

   stderr

      标准错误流 ("StreamReader") 或者如果进程创建时设置了
      "stderr=None" 则为 "None".

   警告:

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

   pid

      进程标识号（PID）。

      请注意对于由 "create_subprocess_shell()" 函数所创建的进程，这个
      属性将是所生成的 shell 的 PID.

   returncode

      进程退出时的返回码。

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

      对于使用 "create_subprocess_exec()" 创建的进程，一个负值 "-N" 表
      示子进程被信号 "N" 终结（仅 POSIX）。

      对于使用 "create_subprocess_shell()" 创建的进程，返回代码反映了
      shell 本身 (例如 "/bin/sh") 的退出状态，它可能会将信号映射到诸如
      "128+N" 的代码。 详情参见 shell 的文档（例如，Bash 文档的退出状
      态说明）。


子进程和线程
------------

标准 asyncio 事件循环默认支持从不同线程中运行子进程。

在 Windows 上子进程（默认）只由 "ProactorEventLoop" 提供，
"SelectorEventLoop" 没有子进程支持。

请注意其他的事件循环实现可能有其本身的限制；请查看它们各自的文档。

参见: asyncio 中的并发和多线程 章节。


例子
----

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

这个子进程是由 "create_subprocess_exec()" 函数创建的:

   import asyncio
   import sys

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

       # 创建子进程；重定向标准输出
       # 至一个管道。
       proc = await asyncio.create_subprocess_exec(
           sys.executable, '-c', code,
           stdout=asyncio.subprocess.PIPE)

       # 读取一行输出。
       data = await proc.stdout.readline()
       line = data.decode('ascii').rstrip()

       # 等待子进程退出。
       await proc.wait()
       return line

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

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