子進(jìn)程集?
源代碼: Lib/asyncio/subprocess.py, Lib/asyncio/base_subprocess.py
本節(jié)介紹了用于創(chuàng)建和管理子進(jìn)程的高層級 async/await asyncio API。
下面的例子演示了如何用 asyncio 運(yùn)行一個 shell 命令并獲取其結(jié)果:
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 子進(jìn)程函數(shù)都是異步的并且 asyncio 提供了許多工具用來配合這些函數(shù)使用,因此并行地執(zhí)行和監(jiān)視多個子進(jìn)程十分容易。 要修改上面的例子來同時運(yùn)行多個命令確實(shí)是非常簡單的:
async def main():
await asyncio.gather(
run('ls /zzz'),
run('sleep 1; echo "hello"'))
asyncio.run(main())
另請參閱 Examples 小節(jié)。
創(chuàng)建子進(jìn)程?
- coroutine asyncio.create_subprocess_exec(program, *args, stdin=None, stdout=None, stderr=None, limit=None, **kwds)?
創(chuàng)建一個子進(jìn)程。
limit 參數(shù)為
Process.stdout
和Process.stderr
設(shè)置StreamReader
包裝器的緩沖區(qū)上限(如果將subprocess.PIPE
傳給了 stdout 和 stderr 參數(shù))。返回一個
Process
實(shí)例。有關(guān)其他形參的說明請查閱
loop.subprocess_exec()
的文檔。在 3.10 版更改: Removed the loop parameter.
- coroutine asyncio.create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, limit=None, **kwds)?
運(yùn)行 cmd shell 命令。
limit 參數(shù)為
Process.stdout
和Process.stderr
設(shè)置StreamReader
包裝器的緩沖區(qū)上限(如果將subprocess.PIPE
傳給了 stdout 和 stderr 參數(shù))。返回一個
Process
實(shí)例。有關(guān)其他形參的說明請查閱
loop.subprocess_shell()
的文檔。重要
應(yīng)用程序要負(fù)責(zé)確保正確地轉(zhuǎn)義所有空白字符和特殊字符以防止 shell 注入 漏洞。
shlex.quote()
函數(shù)可以被用來正確地轉(zhuǎn)義字符串中可以被用來構(gòu)造 shell 命令的空白字符和特殊 shell 字符。在 3.10 版更改: Removed the loop parameter.
備注
如果使用了 ProactorEventLoop
則子進(jìn)程將在 Windows 中可用。 詳情參見 Windows 上的子進(jìn)程支持。
參見
asyncio 還有下列 低層級 API 可配合子進(jìn)程使用: loop.subprocess_exec()
, loop.subprocess_shell()
, loop.connect_read_pipe()
, loop.connect_write_pipe()
以及 子進(jìn)程傳輸 和 子進(jìn)程協(xié)議。
常量?
- asyncio.subprocess.PIPE?
可以被傳遞給 stdin, stdout 或 stderr 形參。
如果 PIPE 被傳遞給 stdin 參數(shù),則
Process.stdin
屬性將會指向一個StreamWriter
實(shí)例。如果 PIPE 被傳遞給 stdout 或 stderr 參數(shù),則
Process.stdout
和Process.stderr
屬性將會指向StreamReader
實(shí)例。
- asyncio.subprocess.STDOUT?
可以用作 stderr 參數(shù)的特殊值,表示標(biāo)準(zhǔn)錯誤應(yīng)當(dāng)被重定向到標(biāo)準(zhǔn)輸出。
- asyncio.subprocess.DEVNULL?
可以用作 stdin, stdout 或 stderr 參數(shù)來處理創(chuàng)建函數(shù)的特殊值。 它表示將為相應(yīng)的子進(jìn)程流使用特殊文件
os.devnull
。
與子進(jìn)程交互?
create_subprocess_exec()
和 create_subprocess_shell()
函數(shù)都返回 Process 類的實(shí)例。 Process 是一個高層級包裝器,它允許與子進(jìn)程通信并監(jiān)視其完成情況。
- class asyncio.subprocess.Process?
一個用于包裝
create_subprocess_exec()
andcreate_subprocess_shell()
函數(shù)創(chuàng)建的 OS 進(jìn)程的對象。這個類被設(shè)計(jì)為具有與
subprocess.Popen
類相似的 API,但兩者有一些重要的差異:不同于 Popen,Process 實(shí)例沒有與
poll()
方法等價的方法;communicate()
和wait()
方法沒有 timeout 形參;要使用wait_for()
函數(shù);Process.wait()
方法是異步的,而subprocess.Popen.wait()
方法則被實(shí)現(xiàn)為阻塞型忙循環(huán);universal_newlines 形參不被支持。
這個類不是線程安全的(not thread safe)。
請參閱 子進(jìn)程和線程 部分。
- coroutine wait()?
等待子進(jìn)程終結(jié)。
設(shè)置并返回
returncode
屬性。備注
當(dāng)使用
stdout=PIPE
或stderr=PIPE
并且子進(jìn)程產(chǎn)生了足以阻塞 OS 管道緩沖區(qū)等待接收更多的數(shù)據(jù)的輸出時,此方法會發(fā)生死鎖。 當(dāng)使用管道時請使用communicate()
方法來避免這種情況。
- coroutine communicate(input=None)?
與進(jìn)程交互:
發(fā)送數(shù)據(jù)到 stdin (如果 input 不為
None
);從 stdout 和 stderr 讀取數(shù)據(jù),直至到達(dá) EOF;
等待進(jìn)程終結(jié)。
可選的 input 參數(shù)為將被發(fā)送到子進(jìn)程的數(shù)據(jù) (
bytes
對象)。返回一個元組
(stdout_data, stderr_data)
。如果在將 input 寫入到 stdin 時引發(fā)了
BrokenPipeError
或ConnectionResetError
異常,異常會被忽略。 此條件會在進(jìn)程先于所有數(shù)據(jù)被寫入到 stdin 之前退出時發(fā)生。如果想要將數(shù)據(jù)發(fā)送到進(jìn)程的 stdin,則創(chuàng)建進(jìn)程時必須使用
stdin=PIPE
。 類似地,要在結(jié)果元組中獲得任何不為None
的值,則創(chuàng)建進(jìn)程時必須使用stdout=PIPE
和/或stderr=PIPE
參數(shù)。注意,數(shù)據(jù)讀取在內(nèi)存中是帶緩沖的,因此如果數(shù)據(jù)量過大或不受則不要使用此方法。
- send_signal(signal)?
將信號 signal 發(fā)送給子進(jìn)程。
備注
在 Windows 上,
SIGTERM
是terminate()
的別名。CTRL_C_EVENT
和CTRL_BREAK_EVENT
可被發(fā)送給創(chuàng)建時設(shè)置了 creationflags 形參且其中包括CREATE_NEW_PROCESS_GROUP
的進(jìn)程。
- terminate()?
停止子進(jìn)程。
在 POSIX 系統(tǒng)中此方法會發(fā)送
signal.SIGTERM
給子進(jìn)程。在 Windows 上會調(diào)用 Win32 API 函數(shù)
TerminateProcess()
以停止子進(jìn)程。
- kill()?
殺掉子進(jìn)程。
在 POSIX 系統(tǒng)中此方法會發(fā)送
SIGKILL
給子進(jìn)程。在 Windows 上此方法是
terminate()
的別名。
- stdin?
標(biāo)準(zhǔn)輸入流 (
StreamWriter
) 或者如果進(jìn)程創(chuàng)建時設(shè)置了stdin=None
則為None
。
- stdout?
標(biāo)準(zhǔn)輸出流 (
StreamReader
) 或者如果進(jìn)程創(chuàng)建時設(shè)置了stdout=None
則為None
。
- stderr?
標(biāo)準(zhǔn)錯誤流 (
StreamReader
) 或者如果進(jìn)程創(chuàng)建時設(shè)置了stderr=None
則為None
。
警告
Use the
communicate()
method rather thanprocess.stdin.write()
,await process.stdout.read()
orawait process.stderr.read()
. This avoids deadlocks due to streams pausing reading or writing and blocking the child process.- pid?
進(jìn)程標(biāo)識號(PID)。
注意對于由Note that for processes created by the
create_subprocess_shell()
函數(shù)所創(chuàng)建的進(jìn)程,這個屬性將是所生成的 shell 的 PID。
- returncode?
當(dāng)進(jìn)程退出時返回其代號。
None
值表示進(jìn)程尚未終止。一個負(fù)值
-N
表示子進(jìn)程被信號N
中斷 (僅 POSIX).
子進(jìn)程和線程?
標(biāo)準(zhǔn) asyncio 事件循環(huán)默認(rèn)支持從不同線程中運(yùn)行子進(jìn)程。
在 Windows 上子進(jìn)程(默認(rèn))只由 ProactorEventLoop
提供,SelectorEventLoop
沒有子進(jìn)程支持。
在 UNIX 上會使用 child watchers 來讓子進(jìn)程結(jié)束等待,詳情請參閱 進(jìn)程監(jiān)視器。
在 3.8 版更改: UNIX 對于從不同線程中無限制地生成子進(jìn)程會切換為使用 ThreadedChildWatcher
。
使用 不活動的 當(dāng)前子監(jiān)視器生成子進(jìn)程將引發(fā) RuntimeError
。
請注意其他的事件循環(huán)實(shí)現(xiàn)可能有其本身的限制;請查看它們各自的文檔。
參見
asyncio 中的并發(fā)和多線程 章節(jié)。
例子?
一個使用 Process
類來控制子進(jìn)程并用 StreamReader
類來從其標(biāo)準(zhǔn)輸出讀取信息的示例。
這個子進(jìn)程是由 create_subprocess_exec()
函數(shù)創(chuàng)建的:
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 編寫的 相同示例。