子程序¶
原始碼: 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
。
請注意,其他事件迴圈實現可能具有自己的限制;請參閱它們的文件。
另請參閱
示例¶
一個使用 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 編寫的相同示例。