目錄

廣告 AD
廣告被擋住了 QAQ

Pexpect:自動化的神奇工具,讓自動化變得更簡單

當初看到 Pexpect 就覺得應該蠻好玩的

這次測試下來也真的蠻好用的

廣告 AD
廣告被擋住了 QAQ

Pexpect 讓你可以透過 Python 跟其他應用程式互動,省去了自己使用 subprocess 和處理輸入輸出的操作,只要透過 library 就可以與之互動,接下來就介紹一些基本的用法。


安裝上直接使用 pip install 就可以了。

pip install pexpect

一般的 Linux 環境下,直接使用 pexpect.spawn

import pexpect

child = pexpect.spawn('ssh -T git@github.com')

目前的版本支援 Windows,但由於 pexpect.spawn 底層使用 Linux 的 pseudoterminals,在 Windows 使用 pexpect.spawn 會出現以下的錯誤,Windows 則是可以使用 pexpect.popen_spawn.PopenSpawn 來運行。

AttributeError: module ‘pexpect’ has no attribute ‘spawn’

Pexcept Document - Pexcept on Windows

import pexpect.popen_spawn

child = pexpect.popen_spawn.PopenSpawn('ssh -T git@github.com')

我們可以用 expect 來等待應用程式回傳給定的字串,給定的字串可以使用正規表達式

並在之後透過 before 和 after 來獲取應用程式的字串

  • before: 回傳 expect 配對到的字串之前的字串
  • after: 回傳 expect 配對到的字串

下面我們用檢測是否能正常用 ssh 登入 github 的指令當作範例,正常輸出為:

Hi bigcat! You've successfully authenticated, but GitHub does not provide shell access.

我們抓取 success 的這個字串,你會看到 before 印出了 success 之前的字串,而 after 則是 success。

import pexpect

child = pexpect.spawn('ssh -T git@github.com')
child.expect("success")
print(f"Before: {child.before.decode()}")
print(f"After: {child.after.decode()}")
Before: Hi bigcat! You've 
After: success

緊接著我們繼續抓取剩餘字串,透過正規表達式,我們抓取了逗號(,)開頭,句點(.)結尾的字串,before 包含著 success 之後到逗號之前的字串,而 after 就是我們要抓取的字串。

import pexpect

child = pexpect.spawn('ssh -T git@github.com')
child.expect("success")
print(f"Before: {child.before.decode()}")
print(f"After: {child.after.decode()}")

child.expect(r",.+\.")
print(f"Before: {child.before.decode()}")
print(f"After: {child.after.decode()}")
Before: Hi bigcat! You've 
After: success
Before: fully authenticated
After: , but GitHub does not provide shell access.

如果我們要直接抓取到 EOF 的所有字串,可以直接傳入 pexpect.EOF,則 after 就會是 pexpect.EOF 的變數,before 存著所有 EOF 之前的字串,包含換行,因此輸出才會空一行。

import pexpect

child = pexpect.spawn('ssh -T git@github.com')
child.expect(pexpect.EOF)
print(f"Before: {child.before.decode()}")
print(f"After: {child.after}")
Before: Hi bigcat! You've successfully authenticated, but GitHub does not provide shell access.

After: <class 'pexpect.exceptions.EOF'>

我們也可以設定 timeout 來設定等待的最長時間,單位為秒,超過會拋出 pexpect.TIMEOUT。

import pexpect

child = pexpect.spawn('ssh -T git@github.com')
try:
    child.expect(pexpect.EOF, timeout=10)
except pexpect.TIMEOUT:
    print("Timeout")
Timeout

另外,我們也可以傳入 list,裡面是各種可能會出現的字串、正規表達式,或是 pexpect.EOFpexpect.TIMEOUT,最後看輸出是符合哪一個,回傳符合的 index,我們可以透過這個 index 來知道應用程式目前的狀態,並依據狀態做出相對的處理。

import pexpect

child = pexpect.spawn('ssh -T git@github.com')
idx = child.expect(["success", "denied", pexpect.TIMEOUT], timeout=10)
match idx:
    case 0:
        print("Login Success")
    case 1:
        print("Login Failed")
    case 2:
        print("Connection Failed")
Login Success

最後,這裡有一點要提醒,在正規表達式的使用上,如果有使用到 *+,因為 expect 會採用最小符合原則,因此只要字串有符合,則會馬上回傳,並不會看後面的字串,我們看例子比較好懂。

持續上面的例子,如果使用 expect(r".*") 則甚麼都抓不到。

import pexpect

child = pexpect.spawn('ssh -T git@github.com')
child.expect(r".*")
print(child.after.decode() == "")
True

expect 讓我們知道如何抓取應用程式的輸出,sendsendline 讓我們可以傳輸入給應用程式,達到跟應用程式互動的功能,sendline 就是多傳送換行的 send

我們一樣用之前的例子,只是如果電腦上沒有儲存 github 的 fingerprint,則會詢問使用者是否要繼續連線,這時候就要輸入 yes 來繼續連線,我們使用 sendline 來達成這個動作。但以下的範例在 Windows 上測試會有問題,建議在 Linux 上執行。

import pexpect

child = pexpect.spawn('ssh -T git@github.com')
while True:
    idx = child.expect(["success", "denied", r"fingerprint.+\?", pexpect.TIMEOUT], timeout=10)
    match idx:
        case 0:
            print("Login Success")
            break
        case 1:
            print("Login Failed")
            break
        case 2:
            print("Store fingerprint")
            child.sendline("yes")
        case 3:
            print("Connection Failed")
            break
Store fingerprint
Login Success

有時候你會想看應用程式的輸出或是為了 debug,你可以使用透過設定 logfilesys.out.buffer,這樣輸出就會顯示在 stdout 上面了。

import pexpect
import sys

child = pexpect.spawn('ssh -T git@github.com')
child.logfile = sys.stdout.buffer
child.expect("success")
Hi bigcat! You've successfully authenticated, but GitHub does not provide shell access.

也可以寫到檔案上,只要將 logfile 設定成檔案就好,記得要用 binary mode。

import pexpect

child = pexpect.spawn('ssh -T git@github.com')
child.logfile = open("output.log", "wb")
child.expect("success")
child.logfile.close()

廣告 AD
廣告被擋住了 QAQ
留言
  • 最新
  • 最早
  • 熱門
Powered by Waline v3.5.7