子程序

原始碼: 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.stdoutProcess.stderr (如果將 subprocess.PIPE 傳遞給 stdoutstderr 引數)。

返回一個 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.stdoutProcess.stderr (如果將 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() 函式建立的 OS 程序的物件。

此類旨在具有與 subprocess.Popen 類類似的 API,但有一些顯著的差異

此類是非執行緒安全的。

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

協程 wait()

等待子程序終止。

設定並返回 returncode 屬性。

注意

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

協程 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 可以傳送到使用 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 編寫的相同示例