子進(jìn)程集?

源代碼: Lib/asyncio/subprocess.py, Lib/asyncio/base_subprocess.py


本節(jié)介紹了用于創(chuàng)建和管理子進(jìn)程的高層級(jí) async/await asyncio API。

下面的例子演示了如何用 asyncio 運(yùn)行一個(gè) 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 提供了許多工具用來(lái)配合這些函數(shù)使用,因此并行地執(zhí)行和監(jiān)視多個(gè)子進(jìn)程十分容易。 要修改上面的例子來(lái)同時(shí)運(yùn)行多個(gè)命令確實(shí)是非常簡(jiǎn)單的:

async def main():
    await asyncio.gather(
        run('ls /zzz'),
        run('sleep 1; echo "hello"'))

asyncio.run(main())

另請(qǐng)參閱 Examples 小節(jié)。

創(chuàng)建子進(jìn)程?

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

創(chuàng)建一個(gè)子進(jìn)程。

limit 參數(shù)為 Process.stdoutProcess.stderr 設(shè)置 StreamReader 包裝器的緩沖區(qū)上限(如果將 subprocess.PIPE 傳給了 stdoutstderr 參數(shù))。

返回一個(gè) Process 實(shí)例。

有關(guān)其他形參的說(shuō)明請(qǐng)查閱 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.stdoutProcess.stderr 設(shè)置 StreamReader 包裝器的緩沖區(qū)上限(如果將 subprocess.PIPE 傳給了 stdoutstderr 參數(shù))。

返回一個(gè) Process 實(shí)例。

有關(guān)其他形參的說(shuō)明請(qǐng)查閱 loop.subprocess_shell() 的文檔。

重要

應(yīng)用程序要負(fù)責(zé)確保正確地轉(zhuǎn)義所有空白字符和特殊字符以防止 shell 注入 漏洞。 shlex.quote() 函數(shù)可以被用來(lái)正確地轉(zhuǎn)義字符串中可以被用來(lái)構(gòu)造 shell 命令的空白字符和特殊 shell 字符。

在 3.10 版更改: Removed the loop parameter.

備注

如果使用了 ProactorEventLoop 則子進(jìn)程將在 Windows 中可用。 詳情參見(jiàn) Windows 上的子進(jìn)程支持。

參見(jiàn)

asyncio 還有下列 低層級(jí) 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, stdoutstderr 形參。

如果 PIPE 被傳遞給 stdin 參數(shù),則 Process.stdin 屬性將會(huì)指向一個(gè) StreamWriter 實(shí)例。

如果 PIPE 被傳遞給 stdoutstderr 參數(shù),則 Process.stdoutProcess.stderr 屬性將會(huì)指向 StreamReader 實(shí)例。

asyncio.subprocess.STDOUT?

可以用作 stderr 參數(shù)的特殊值,表示標(biāo)準(zhǔn)錯(cuò)誤應(yīng)當(dāng)被重定向到標(biāo)準(zhǔn)輸出。

asyncio.subprocess.DEVNULL?

可以用作 stdin, stdoutstderr 參數(shù)來(lái)處理創(chuàng)建函數(shù)的特殊值。 它表示將為相應(yīng)的子進(jìn)程流使用特殊文件 os.devnull。

與子進(jìn)程交互?

create_subprocess_exec()create_subprocess_shell() 函數(shù)都返回 Process 類的實(shí)例。 Process 是一個(gè)高層級(jí)包裝器,它允許與子進(jìn)程通信并監(jiān)視其完成情況。

class asyncio.subprocess.Process?

一個(gè)用于包裝 create_subprocess_exec() and create_subprocess_shell() 函數(shù)創(chuàng)建的 OS 進(jìn)程的對(duì)象。

這個(gè)類被設(shè)計(jì)為具有與 subprocess.Popen 類相似的 API,但兩者有一些重要的差異:

  • 不同于 Popen,Process 實(shí)例沒(méi)有與 poll() 方法等價(jià)的方法;

  • communicate()wait() 方法沒(méi)有 timeout 形參;要使用 wait_for() 函數(shù);

  • Process.wait() 方法是異步的,而 subprocess.Popen.wait() 方法則被實(shí)現(xiàn)為阻塞型忙循環(huán);

  • universal_newlines 形參不被支持。

這個(gè)類不是線程安全的(not thread safe)。

請(qǐng)參閱 子進(jìn)程和線程 部分。

coroutine wait()?

等待子進(jìn)程終結(jié)。

設(shè)置并返回 returncode 屬性。

備注

當(dāng)使用 stdout=PIPEstderr=PIPE 并且子進(jìn)程產(chǎn)生了足以阻塞 OS 管道緩沖區(qū)等待接收更多的數(shù)據(jù)的輸出時(shí),此方法會(huì)發(fā)生死鎖。 當(dāng)使用管道時(shí)請(qǐng)使用 communicate() 方法來(lái)避免這種情況。

coroutine communicate(input=None)?

與進(jìn)程交互:

  1. 發(fā)送數(shù)據(jù)到 stdin (如果 input 不為 None);

  2. stdoutstderr 讀取數(shù)據(jù),直至到達(dá) EOF;

  3. 等待進(jìn)程終結(jié)。

可選的 input 參數(shù)為將被發(fā)送到子進(jìn)程的數(shù)據(jù) (bytes 對(duì)象)。

返回一個(gè)元組 (stdout_data, stderr_data)

如果在將 input 寫入到 stdin 時(shí)引發(fā)了 BrokenPipeErrorConnectionResetError 異常,異常會(huì)被忽略。 此條件會(huì)在進(jìn)程先于所有數(shù)據(jù)被寫入到 stdin 之前退出時(shí)發(fā)生。

如果想要將數(shù)據(jù)發(fā)送到進(jìn)程的 stdin,則創(chuàng)建進(jìn)程時(shí)必須使用 stdin=PIPE。 類似地,要在結(jié)果元組中獲得任何不為 None 的值,則創(chuàng)建進(jìn)程時(shí)必須使用 stdout=PIPE 和/或 stderr=PIPE 參數(shù)。

注意,數(shù)據(jù)讀取在內(nèi)存中是帶緩沖的,因此如果數(shù)據(jù)量過(guò)大或不受則不要使用此方法。

send_signal(signal)?

將信號(hào) signal 發(fā)送給子進(jìn)程。

備注

在 Windows 上,SIGTERMterminate() 的別名。 CTRL_C_EVENTCTRL_BREAK_EVENT 可被發(fā)送給創(chuàng)建時(shí)設(shè)置了 creationflags 形參且其中包括 CREATE_NEW_PROCESS_GROUP 的進(jìn)程。

terminate()?

停止子進(jìn)程。

在 POSIX 系統(tǒng)中此方法會(huì)發(fā)送 signal.SIGTERM 給子進(jìn)程。

在 Windows 上會(huì)調(diào)用 Win32 API 函數(shù) TerminateProcess() 以停止子進(jìn)程。

kill()?

殺掉子進(jìn)程。

在 POSIX 系統(tǒng)中此方法會(huì)發(fā)送 SIGKILL 給子進(jìn)程。

在 Windows 上此方法是 terminate() 的別名。

stdin?

標(biāo)準(zhǔn)輸入流 (StreamWriter) 或者如果進(jìn)程創(chuàng)建時(shí)設(shè)置了 stdin=None 則為 None

stdout?

標(biāo)準(zhǔn)輸出流 (StreamReader) 或者如果進(jìn)程創(chuàng)建時(shí)設(shè)置了 stdout=None 則為 None。

stderr?

標(biāo)準(zhǔn)錯(cuò)誤流 (StreamReader) 或者如果進(jìn)程創(chuàng)建時(shí)設(shè)置了 stderr=None 則為 None

警告

Use the communicate() method rather than process.stdin.write(), await process.stdout.read() or await process.stderr.read(). This avoids deadlocks due to streams pausing reading or writing and blocking the child process.

pid?

進(jìn)程標(biāo)識(shí)號(hào)(PID)。

注意對(duì)于由Note that for processes created by the create_subprocess_shell() 函數(shù)所創(chuàng)建的進(jìn)程,這個(gè)屬性將是所生成的 shell 的 PID。

returncode?

當(dāng)進(jìn)程退出時(shí)返回其代號(hào)。

None 值表示進(jìn)程尚未終止。

一個(gè)負(fù)值 -N 表示子進(jìn)程被信號(hào) N 中斷 (僅 POSIX).

子進(jìn)程和線程?

標(biāo)準(zhǔn) asyncio 事件循環(huán)默認(rèn)支持從不同線程中運(yùn)行子進(jìn)程。

在 Windows 上子進(jìn)程(默認(rèn))只由 ProactorEventLoop 提供,SelectorEventLoop 沒(méi)有子進(jìn)程支持。

在 UNIX 上會(huì)使用 child watchers 來(lái)讓子進(jìn)程結(jié)束等待,詳情請(qǐng)參閱 進(jìn)程監(jiān)視器

在 3.8 版更改: UNIX 對(duì)于從不同線程中無(wú)限制地生成子進(jìn)程會(huì)切換為使用 ThreadedChildWatcher。

使用 不活動(dòng)的 當(dāng)前子監(jiān)視器生成子進(jìn)程將引發(fā) RuntimeError。

請(qǐng)注意其他的事件循環(huán)實(shí)現(xiàn)可能有其本身的限制;請(qǐng)查看它們各自的文檔。

參見(jiàn)

asyncio 中的并發(fā)和多線程 章節(jié)。

例子?

一個(gè)使用 Process 類來(lái)控制子進(jìn)程并用 StreamReader 類來(lái)從其標(biāo)準(zhǔn)輸出讀取信息的示例。

這個(gè)子進(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}")

另請(qǐng)參閱使用低層級(jí) API 編寫的 相同示例。