子进程 — Python 文档

来自菜鸟教程
Python/docs/3.7/library/asyncio-subprocess
跳转至:导航、​搜索

子流程

本节介绍用于创建和管理子进程的高级 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())

另请参阅 示例 小节。

创建子流程

重要的

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


笔记

Windows 上的默认异步事件循环实现不支持子进程。 如果使用 ProactorEventLoop,则子进程可用于 Windows。 有关详细信息,请参阅 Windows 上的子进程支持 [X37X]。


也可以看看

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,但有一些显着差异:

  • 与 Popen 不同,Process 实例没有与 poll() 方法等效的方法;

  • communicate()wait() 方法没有 timeout 参数:使用 wait_for() 函数;

  • Process.wait() 方法是异步的,而 subprocess.Popen.wait() 方法是作为阻塞忙循环实现的;

  • 不支持 universal_newlines 参数。

此类是 不是线程安全的

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

send_signal(signal)

向子进程发送信号signal

笔记

在 Windows 上,SIGTERMterminate() 的别名。 CTRL_C_EVENTCTRL_BREAK_EVENT 可以发送到以 creationflags 参数启动的进程,该参数包括 CREATE_NEW_PROCESS_GROUP

terminate()

停止子进程。

在 POSIX 系统上,此方法将 signal.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[ X138X]。 这避免了由于流暂停读取或写入并阻塞子进程而导致的死锁。

pid

进程标识号 (PID)。

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

returncode

退出时返回进程的代码。

None 值表示进程尚未终止。

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

子进程和线程

标准的 asyncio 事件循环支持从不同线程运行子进程,但有一些限制:

  • 事件循环必须在主线程中运行。
  • 在从其他线程执行子进程之前,必须在主线程中实例化子观察者。 在主线程中调用get_child_watcher()函数来实例化子观察者。

请注意,替代事件循环实现可能不具有上述限制; 请参考他们的文档。

也可以看看

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

if sys.platform == "win32":
    asyncio.set_event_loop_policy(
        asyncio.WindowsProactorEventLoopPolicy())

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

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