子进程¶
源代码: 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.stdout
和Process.stderr
(如果将subprocess.PIPE
传递给 stdout 和 stderr 参数)。返回一个
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.stdout
和Process.stderr
(如果将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 参数。
此类是非线程安全的。
另请参阅子进程与线程部分。
- 协程 wait()¶
等待子进程终止。
设置并返回
returncode
属性。注意
当使用
stdout=PIPE
或stderr=PIPE
且子进程生成大量输出,导致操作系统管道缓冲区阻塞,无法接受更多数据时,此方法可能会死锁。使用管道时,请使用communicate()
方法以避免这种情况。
- 协程 communicate(input=None)¶
与进程交互
将数据发送到 stdin (如果 input 不是
None
);关闭 stdin;
从 stdout 和 stderr 读取数据,直到到达 EOF;
等待进程终止。
可选的 input 参数是将发送到子进程的数据(
bytes
对象)。返回一个元组
(stdout_data, stderr_data)
。如果在将 input 写入 stdin 时引发
BrokenPipeError
或ConnectionResetError
异常,则会忽略该异常。当进程在所有数据写入 stdin 之前退出时,会发生这种情况。如果希望将数据发送到进程的 stdin,则必须使用
stdin=PIPE
创建该进程。类似地,要使结果元组中得到除None
以外的任何值,必须使用stdout=PIPE
和/或stderr=PIPE
参数创建该进程。请注意,读取的数据会缓存在内存中,因此如果数据大小很大或无限大,请不要使用此方法。
在 3.12 版本中更改: 当 input=None 时,stdin 也会被关闭。
- 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
) 或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 编写的相同示例。