天天看點

Python 異步: 在非阻塞子程序中運作指令(19)

作者:冷凍工廠

動動發财的小手,點個贊吧!

我們可以從 asyncio 執行指令。該指令将在我們可以使用非阻塞 I/O 寫入和讀取的子程序中運作。

1. 什麼是 asyncio.subprocess.Process

asyncio.subprocess.Process 類提供了由 asyncio 運作的子程序的表示。它在 asyncio 程式中提供子程序的句柄,允許對其執行操作,例如等待和終止它。

該 API 與 multiprocessing.Process 類非常相似,可能與 subprocess.Popen 類更相似。具體來說,它與 subprocess.Popen 共享 wait()、communicate() 和 send_signal() 等方法以及 stdin、stdout 和 stderr 等屬性。

現在我們知道了 asyncio.subprocess.Process 類是什麼,讓我們看看如何在我們的 asyncio 程式中使用它。

我們不直接建立 asyncio.subprocess.Process。相反,在 asyncio 程式中執行子程序時,會為我們建立一個類的執行個體。

有兩種方法可以将外部程式作為子流程執行并擷取 Process 執行個體,它們是:

  • asyncio.create_subprocess_exec() 用于直接運作指令。
  • asyncio.create_subprocess_shell() 用于通過 shell 運作指令。

讓我們依次看一下每個示例。

2. 如何直接運作指令

指令是在指令行(終端或指令提示符)上執行的程式。這是另一個直接運作的程式。

Linux 和 macOS 上的常見示例可能是:

  • ‘ls’ 列出目錄的内容
  • ‘cat’報告檔案的内容
  • “data”報告日期
  • ‘echo’ 報告一個字元串
  • ‘sleep’ 睡眠幾秒鐘

我們可以通過 create_subprocess_exec() 函數從 asyncio 程式執行指令。

asyncio.create_subprocess_exec() 函數接受一個指令并直接執行它。

這很有用,因為它允許指令在子程序中執行,并允許 asyncio 協程讀取、寫入和等待它。

與 asyncio.create_subprocess_shell() 函數不同,asyncio.create_subprocess_exec() 不會使用 shell 執行指令。

這意味着 shell 提供的功能,例如 shell 變量、腳本和通配符,在執行指令時不可用。

這也意味着執行指令可能更安全,因為沒有機會進行 shell 注入。

現在我們知道了 asyncio.create_subprocess_exec() 的作用,讓我們看看如何使用它。

2.1. 如何使用 Asyncio create_subprocess_exec()

asyncio.create_subprocess_exec() 函數将在子程序中執行給定的字元串指令。

它傳回一個代表子程序的 asyncio.subprocess.Process 對象。

create_subprocess_exec() 函數是一個協程,這意味着我們必須等待它。它會在子流程啟動後傳回,而不是在子流程完成時傳回。

...
# execute a command in a subprocess
process = await asyncio.create_subprocess_exec('ls')
           

正在執行的指令的參數必須作為後續參數提供給 create_subprocess_exec() 函數。

...
# execute a command with arguments in a subprocess
process = await asyncio.create_subprocess_exec('ls', '-l')
           

我們可以通過等待 wait() 方法來等待子程序完成。

...
# wait for the subprocess to terminate
await process.wait()
           

我們可以通過調用 terminate() 或 kill() 方法直接停止子程序,這将在子程序中引發一個信号。

...
# terminate the subprocess
process.terminate()
           

指令的輸入和輸出将由 stdin、stderr 和 stdout 處理。我們可以讓 asyncio 程式處理子程序的輸入或輸出。

這可以通過指定輸入或輸出流并指定要重定向的常量來實作,例如 asyncio.subprocess.PIPE。

例如,我們可以将指令的輸出重定向到 asyncio 程式:

...
# start a subprocess and redirect output
process = await asyncio.create_subprocess_exec('ls', stdout=asyncio.subprocess.PIPE)
           

然後我們可以通過 asyncio.subprocess.Process 執行個體通過 communicate() 方法讀取程式的輸出。

此方法是協程,必須等待。它用于通過子流程發送和接收資料。

...
# read data from the subprocess
line = process.communicate()
           

我們還可以通過以位元組為機關設定“input”參數,通過 communicate() 方法将資料發送到子程序。

...
# start a subprocess and redirect input
process = await asyncio.create_subprocess_exec('ls', stdin=asyncio.subprocess.PIPE)
# send data to the subprocess
process.communicate(input=b'Hello\n')
           

在背景,asyncio.subprocess.PIPE 将子程序配置為指向 StreamReader 或 StreamWriter,用于向子程序發送資料或從子程序發送資料,并且 communicate() 方法将從配置的讀取器讀取或寫入位元組。

我們可以通過子程序通過 stdin、stdout 和 stderr 屬性直接與 StreamReader 或 StreamWriter 互動。

...
# read a line from the subprocess output stream
line = await process.stdout.readline()
           

現在我們知道如何使用 create_subprocess_exec() 函數,讓我們看一些工作示例。

2.2. Asyncio create_subprocess_exec() 示例

我們可以探索如何在 asyncio 的子程序中運作指令。在這個例子中,我們将執行“echo”指令來報告一個字元串。

echo 指令将直接在标準輸出上報告提供的字元串。下面列出了完整的示例。

請注意,此示例假設您可以通路“echo”指令,我不确定它是否适用于 Windows。

# SuperFastPython.com
# example of executing a command as a subprocess with asyncio
import asyncio
 
# main coroutine
async def main():
    # start executing a command in a subprocess
    process = await asyncio.create_subprocess_exec('echo', 'Hello World')
    # report the details of the subprocess
    print(f'subprocess: {process}')
 
# entry point
asyncio.run(main())
           

運作示例首先建立 main() 協程并将其作為 asyncio 程式的入口點執行。

main() 協程運作并調用 create_subprocess_exec() 函數來執行指令。

main() 協程在建立子程序時挂起。傳回一個 Process 執行個體。

main() 協程恢複并報告子程序的詳細資訊。 main() 程序終止,asyncio 程式終止。

echo 指令的輸出在指令行上報告。這突出了我們如何從 asyncio 程式執行指令。

Hello World
subprocess: <Process 50249>
           

3. 如何通過 Shell 運作指令

我們可以使用 shell 執行指令。shell 是指令行的使用者界面,稱為指令行解釋器 (CLI)。它将代表使用者解釋和執行指令。

它還提供諸如用于腳本、通配符、管道、shell 變量(例如 PATH)等的原始程式設計語言等功能。

例如,我們可以将一條指令的輸出重定向為另一條指令的輸入,比如将“/etc/services”檔案的内容重定向到word count指令“wc”中,統計行數:

cat /etc/services | wc -l
           

基于 Unix 的作業系統中的 shell 示例包括:

Python 異步: 在非阻塞子程式中運作指令(19)

shell 已經在運作,它被用來啟動 Python 程式。您無需執行任何特殊操作即可擷取或通路 shell。

我們可以通過 create_subprocess_shell() 函數從 asyncio 程式執行指令。

asyncio.create_subprocess_shell() 函數接受一個指令并使用目前使用者 shell 執行它。

這很有用,因為它不僅允許執行指令,還允許使用 shell 的功能,例如重定向、通配符等。

該指令将在執行 asyncio 程式的程序的子程序中執行。重要的是,asyncio 程式能夠與子程序異步互動,例如通過協程。

通過 shell 而不是直接執行指令時,可能會有安全考慮。

這是因為請求執行指令和正在執行的指令之間至少存在一層間接和解釋,允許可能的惡意注入。

現在我們知道了 asyncio.create_subprocess_shell() 的作用,讓我們看看如何使用它。

3.1. 如何使用 Asyncio create_subprocess_shell()

asyncio.create_subprocess_shell() 函數将通過目前 shell 執行給定的字元串指令。

它傳回一個表示程序的 asyncio.subprocess.Process 對象。

它與我們在上一節中看到的 create_subprocess_shell() 函數非常相似。不過,我們将回顧如何使用該函數以及如何通過 Process 執行個體與流程互動(以防您直接跳到本節)。

create_subprocess_shell() 函數是一個協程,這意味着我們必須等待它。它會在子流程啟動後傳回,而不是在子流程完成時傳回。

...
# start a subprocess
process = await asyncio.create_subprocess_shell('ls')
           

我們可以通過等待 wait() 方法來等待子程序完成。

...
# wait for the subprocess to terminate
await process.wait()
           

我們可以通過調用 terminate() 或 kill() 方法直接停止子程序,這将在子程序中引發一個信号。

指令的輸入和輸出将由 shell 處理,例如标準輸入、标準錯誤和标準輸出。

我們可以讓 asyncio 程式處理子程序的輸入或輸出。

這可以通過指定輸入或輸出流并指定要重定向的常量來實作,例如 asyncio.subprocess.PIPE。

例如,我們可以将指令的輸出重定向到 asyncio 程式:

...
# start a subprocess and redirect output
process = await asyncio.create_subprocess_shell('ls', stdout=asyncio.subprocess.PIPE)
           

然後我們可以通過 asyncio.subprocess.Process 執行個體通過 communicate() 方法讀取程式的輸出。

此方法是協程,必須等待。它用于通過子流程發送和接收資料。

...
# read data from the subprocess
line = process.communicate()
           

我們還可以通過以位元組為機關設定“input”參數,通過 communicate() 方法将資料發送到子程序。

...
# start a subprocess and redirect input
process = await asyncio.create_subprocess_shell('ls', stdin=asyncio.subprocess.PIPE)
# send data to the subprocess
process.communicate(input=b'Hello\n')
           

在背景,asyncio.subprocess.PIPE 将子程序配置為指向 StreamReader 或 StreamWriter,用于向子程序發送資料或從子程序發送資料,并且 communicate() 方法将從配置的讀取器讀取或寫入位元組。

我們可以通過子程序通過 stdin、stdout 和 stderr 屬性直接與 StreamReader 或 StreamWriter 互動。

...
# read a line from the subprocess output stream
line = await process.stdout.readline()
           

現在我們知道如何使用 create_subprocess_shell() 函數,讓我們看一些工作示例。

3.2. Asyncio create_subprocess_shell() 示例

我們可以探索如何使用 shell 在 asyncio 的子程序中運作指令。在這個例子中,我們将執行“echo”指令來報告一個字元串。

echo 指令将直接在标準輸出上報告提供的字元串。下面列出了完整的示例。

請注意,此示例假設您可以通路“echo”指令,我不确定它是否适用于 Windows。

# SuperFastPython.com
# example of executing a shell command as a subprocess with asyncio
import asyncio
 
# main coroutine
async def main():
    # start executing a shell command in a subprocess
    process = await asyncio.create_subprocess_shell('echo Hello World')
    # report the details of the subprocess
    print(f'subprocess: {process}')
 
# entry point
asyncio.run(main())
           

運作示例首先建立 main() 協程并将其作為 asyncio 程式的入口點執行。main() 協程運作并調用 create_subprocess_shell() 函數來執行指令。

main() 協程運作并調用 create_subprocess_shell() 函數來執行指令。main() 協程恢複并報告子程序的詳細資訊。 main() 程序終止,asyncio 程式終止。

echo 指令的輸出在指令行上報告。這突出顯示了我們如何使用 shell 從 asyncio 程式執行指令。

subprocess: <Process 43916>
Hello World