子程序

原始碼: 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())

另請參閱 示例 小節。

建立子程序

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

建立一個子程序。

limit 引數設定 StreamReader 包裝器在 stdoutstderr 處的緩衝區限制(如果 subprocess.PIPE 傳遞給 stdoutstderr 引數)。

返回 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 引數設定 StreamReader 包裝器在 stdoutstderr 處的緩衝區限制(如果 subprocess.PIPE 傳遞給 stdoutstderr 引數)。

返回 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

可傳遞給 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,但存在一些顯著差異

此類 不是執行緒安全的

另請參閱 子程序和執行緒 部分。

async wait()

等待子程序終止。

設定並返回 returncode 屬性。

備註

當使用 stdout=PIPEstderr=PIPE 且子程序生成過多輸出以至於在等待作業系統管道緩衝區接受更多資料時阻塞時,此方法可能會死鎖。使用管道時,請使用 communicate() 方法以避免此情況。

async communicate(input=None)

與程序互動

  1. 將資料傳送到 stdin(如果 input 不為 None);

  2. 關閉 stdin

  3. stdoutstderr 讀取資料,直到達到 EOF;

  4. 等待程序終止。

可選的 input 引數是將傳送到子程序的資料(bytes 物件)。

返回元組 (stdout_data, stderr_data)

如果在將 input 寫入 stdin 時引發 BrokenPipeErrorConnectionResetError 異常,則該異常將被忽略。當程序在所有資料寫入 stdin 之前退出時,會發生這種情況。

如果希望向程序的 stdin 傳送資料,則需要使用 stdin=PIPE 建立程序。同樣,要在結果元組中獲取除 None 之外的任何內容,必須使用 stdout=PIPE 和/或 stderr=PIPE 引數建立程序。

請注意,讀取的資料在記憶體中緩衝,因此如果資料大小較大或不受限制,請勿使用此方法。

3.12 版本中的變化: input=None 時,stdin 也會關閉。

send_signal(signal)

向子程序傳送訊號 signal

備註

在 Windows 上,SIGTERMterminate() 的別名。CTRL_C_EVENTCTRL_BREAK_EVENT 可以傳送到透過包含 CREATE_NEW_PROCESS_GROUPcreationflags 引數啟動的程序。

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 不支援子程序。

請注意,替代事件迴圈實現可能存在自己的限制;請參閱其文件。

示例

一個使用 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 編寫的 相同示例