python pp模块_Python模块--Pexpect
探索 Pexpect,第 1 部分:剖析 Pexpect
概述
Pexpect 是 Don Libes 的?Expect 語言的一個 Python 實現(xiàn),是一個用來啟動子程序,并使用正則表達(dá)式對程序輸出做出特定響應(yīng),以此實現(xiàn)與其自動交互的 Python 模塊。 Pexpect 的使用范圍很廣,可以用來實現(xiàn)與 ssh、ftp 、telnet 等程序的自動交互;可以用來自動復(fù)制軟件安裝包并在不同機器自動安裝;還可以用來實現(xiàn)軟件測試中與命令行交互的自動化。
下載
Pexpect 可以從?SourceForge?網(wǎng)站下載。 本文介紹的示例使用的是 2.3 版本,如不說明測試環(huán)境,默認(rèn)運行操作系統(tǒng)為 fedora 9 并使用 Python 2.5 。
安裝
依賴
Python 版本 2.4 或者 2.5
pty module ,pty 是任何 Posix 系統(tǒng)標(biāo)準(zhǔn)庫的一部分
由于其依賴 pty module ,所以 Pexpect 還不能在 Windows 的標(biāo)準(zhǔn) python 環(huán)境中執(zhí)行,如果想在 Windows 平臺使用,可以使用在 Windows 中運行 Cygwin 做為替代方案。
遵循 MIT 許可證
根據(jù) Wiki 對 MIT License 的介紹“該模塊被授權(quán)人有權(quán)利使用、復(fù)制、修改、合并、出版發(fā)行、散布、再授權(quán)及販?zhǔn)圮浖败浖母北尽1皇跈?quán)人可根據(jù)程序的需要修改授權(quán)條款為適當(dāng)?shù)膬?nèi)容。在軟件和軟件的所有副本中都必須包含版權(quán)聲明和許可聲明。”
Pexpect 提供的 run() 函數(shù):
清單 1. run() 的定義
函數(shù) run 可以用來運行命令,其作用與 Python os 模塊中 system() 函數(shù)相似。run() 是通過?Pexpect類實現(xiàn)的。
如果命令的路徑?jīng)]有完全給出,則 run 會使用 which 命令嘗試搜索命令的路徑 。
清單 2. 使用 run()執(zhí)行 svn 命令
與 os.system() 不同的是,使用 run() 可以方便地同時獲得命令的輸出結(jié)果與命令的退出狀態(tài) 。
清單 3. run() 的返回值
command_out 中保存的就是 /bin 目錄下的內(nèi)容。
Pexpect 提供的 spawn() 類:
使用 Pexpect 啟動子程序
清單 4. spawn 的構(gòu)造函數(shù)
spawn是Pexpect模塊主要的類,用以實現(xiàn)啟動子程序,它有豐富的方法與子程序交互從而實現(xiàn)用戶對子程序的控制。它主要使用 pty.fork() 生成子進程,并調(diào)用 exec() 系列函數(shù)執(zhí)行 command 參數(shù)的內(nèi)容。
可以這樣使用:
清單 5. spawn() 使用示例
當(dāng)子程序需要參數(shù)時,還可以使用一個參數(shù)的列表:
清單 6. 參數(shù)列表示例
在構(gòu)造函數(shù)中,maxread 屬性指定了 Pexpect 對象試圖從 tty 一次讀取的最大字節(jié)數(shù),它的默認(rèn)值是2000字節(jié) 。
由于需要實現(xiàn)不斷匹配子程序輸出, searchwindowsize 指定了從輸入緩沖區(qū)中進行模式匹配的位置,默認(rèn)從開始匹配。
logfile 參數(shù)指定了 Pexpect 產(chǎn)生的日志的記錄位置。
例如:
清單 7. 記錄日志
還可以將日志指向標(biāo)準(zhǔn)輸出:
清單 8. 將日志指向標(biāo)準(zhǔn)輸出
如果不需要記錄向子程序輸入的日志,只記錄子程序的輸出,可以使用:
清單 9. 記錄輸出日志
使用 Pexpect 控制子程序
為了控制子程序,等待子程序產(chǎn)生特定輸出,做出特定的響應(yīng),可以使用 expect 方法。
清單 10. expect() 定義
在參數(shù)中: pattern 可以是正則表達(dá)式, pexpect.EOF , pexpect.TIMEOUT ,或者由這些元素組成的列表。需要注意的是,當(dāng) pattern 的類型是一個列表時,且子程序輸出結(jié)果中不止一個被匹配成功,則匹配返回的結(jié)果是緩沖區(qū)中最先出現(xiàn)的那個元素,或者是列表中最左邊的元素。使用 timeout 可以指定等待結(jié)果的超時時間 ,該時間以秒為單位。當(dāng)超過預(yù)訂時間時, expect 匹配到pexpect.TIMEOUT。
如果難以估算程序運行的時間,可以使用循環(huán)使其多次等待直至等待運行結(jié)束:
清單 11. 使用循環(huán)
expect() 在執(zhí)行中可能會拋出兩種類型的異常分別是 EOF and TIMEOUF,其中 EOF 通常代表子程序的退出, TIMEOUT 代表在等待目標(biāo)正則表達(dá)式中出現(xiàn)了超時。
清單 12. 使用并捕獲異常
此時可以將這兩種異常放入expect等待的目標(biāo)列表中:
清單 13. 避免異常
expect 不斷從讀入緩沖區(qū)中匹配目標(biāo)正則表達(dá)式,當(dāng)匹配結(jié)束時 pexpect 的 before 成員中保存了緩沖區(qū)中匹配成功處之前的內(nèi)容, pexpect 的 after 成員保存的是緩沖區(qū)中與目標(biāo)正則表達(dá)式相匹配的內(nèi)容。
清單 14. 打印 before 成員的內(nèi)容
此時 child.before 保存的就是在根目錄下執(zhí)行 ls 命令的結(jié)果。
清單 15. send 系列函數(shù)
這些方法用來向子程序發(fā)送命令,模擬輸入命令的行為。 與 send() 不同的是 sendline() 會額外輸入一個回車符 ,更加適合用來模擬對子程序進行輸入命令的操作。 當(dāng)需要模擬發(fā)送 “Ctrl+c” 的行為時,還可以使用 sendcontrol() 發(fā)送控制字符。
清單 16. 發(fā)送 ctrl+c
由于 send() 系列函數(shù)向子程序發(fā)送的命令會在終端顯示,所以也會在子程序的輸入緩沖區(qū)中出現(xiàn),因此不建議使用 expect 匹配最近一次 sendline() 中包含的字符。否則可能會在造成不希望的匹配結(jié)果。
清單 17. interact() 定義
Pexpect還可以調(diào)用interact() 讓出控制權(quán),用戶可以繼續(xù)當(dāng)前的會話控制子程序。用戶可以敲入特定的退出字符跳出,其默認(rèn)值為“^]” 。
下面展示一個使用Pexpect和ftp交互的實例。
清單 18. ftp 交互的實例:
該程序與 ftp 做交互,登錄到 ftp.openbsd.org ,當(dāng)提述輸入登錄名稱和密碼時輸入默認(rèn)用戶名和密碼,當(dāng)出現(xiàn) “ftp>” 這一提示符時切換到 pub/OpenBSD 目錄并下載 README 這一文件。
以下實例是上述方法的綜合應(yīng)用,用來建立一個到遠(yuǎn)程服務(wù)器的 telnet 連接,并返回保存該連接的 pexpect 對象。
清單 19. 登錄函數(shù):
Pxssh 類的使用:
Pxssh 做為 pexpect 的派生類可以用來建立一個 ssh 連接,它相比其基類增加了如下方法:
login() 建立到目標(biāo)機器的ssh連接 ;
losuckgout() 釋放該連接 ;
prompt() 等待提示符,通常用于等待命令執(zhí)行結(jié)束。
下面的示例連接到一個遠(yuǎn)程服務(wù)器,執(zhí)行命令并打印命令執(zhí)行結(jié)果。
該程序首先接受用戶輸入用戶名和密碼,login 函數(shù)返回一個 pxssh 對象的鏈接,然后調(diào)用 sendline() 分別輸入 “uptime”、“l(fā)s” 等命令并打印命令輸出結(jié)果。
清單 20. pxssh 示例
Pexpect 使用中需要注意的問題:
spawn() 參數(shù)的限制
在使用spawn執(zhí)行命令時應(yīng)該注意,Pexpect 并不與 shell 的元字符例如重定向符號 > 、>> 、管道 | ,還有通配符 * 等做交互,所以當(dāng)想運行一個帶有管道的命令時必須另外啟動一個 shell ,為了使代碼清晰,以下示例使用了參數(shù)列表例如:
清單 21. 啟動新的 shell 執(zhí)行命令
與線程共同工作
Perl 也有 expect 的模塊?Expect-1.21,但是 perl 的該模塊在某些操作系統(tǒng)例如 fedora 9 或者 AIX 5 中不支持在線程中啟動程序執(zhí)行以下實例試圖利用多線同時程登錄到兩臺機器進行操作,不使用線程直接調(diào)用時 sub1() 函數(shù)可以正常工作,但是使用線程時在 fedora9 和 AIX 5 中都不能正常運行。
清單 22. perl 使用 expect 由于線程和 expect 共同使用導(dǎo)致不能正常工作的程序
Pexpect 則沒有這樣的問題,可以使用多線程并在線程中啟動程序運行。但是在某些操作系統(tǒng)如 fedora9 中不可以在線程之間傳遞 Pexpect 對象。
清單 使用 Pexpect 在線程中啟動控制子程序
應(yīng)用實例:
在實際系統(tǒng)管理員的任務(wù)中,有時需要同時管理多臺機器,這個示例程序被用來自動編譯并安裝新的內(nèi)核版本,并重啟。它使用多線程,每個線程都建立一個到遠(yuǎn)程機器的 telnet 連接并執(zhí)行相關(guān)命令。 該示例會使用上文中的登錄函數(shù)。
清單 23. 管理多臺機器示例
相關(guān)主題
訪問?Expect?的主頁,了解更多 Expect 的內(nèi)容。
參考?Wiki?上關(guān)于 MIT 許可證的介紹。
查看文章“ Expect 在網(wǎng)絡(luò)管理中發(fā)揮著重要作用”介紹 Expect 在網(wǎng)絡(luò)管理中的應(yīng)用。
Perl 語言中的 Expect?了解 Perl 語言中的 Expect 。
在 developerWorks 上查閱所有?Linux 技巧和?Linux 教程。
探索 Pexpect,第 2 部分:Pexpect 的實例分析
概述
通過本系列第一部分《探索 Pexpect,第 1 部分:剖析 Pexpect 》(請參閱參考資料)的介紹,相信大家已經(jīng)對 Pexpect 的用法已經(jīng)有了比較全面的了解,知道 Pexpect 是個純 Python 語言實現(xiàn)的模塊,使用其可以輕松方便的實現(xiàn)與 ssh、ftp、passwd 和 telnet 等程序的自動交互,但是讀者的理解還可能只是停留在理論基礎(chǔ)上,本文將從實際例子入手具體介紹 Pexpect 的使用場景和使用心得體驗,實例中的代碼讀者都可以直接拿來使用,相信會對大家產(chǎn)生比較大的幫助。 以下是本文所要介紹的所有 Pexpect 例子標(biāo)題:
例 1:ftp 的使用(注:spawn、expect 和 sendline 的使用)
例 2:記錄 log(注:logfile、logfile_send和logfile_read的使用)
例 3:ssh 的使用
例 4:pxssh 的使用
例 5:telnet 的使用(注:interact 的使用)
pexpect 使用 tips
調(diào)試 pexpect 程序的 tips
pexpect 不會解釋 shell 中的元字符
EOF 異常和 TIMEOUT 異常
使用 run() 來替代某些的 spawn 的使用
expect_exact() 的使用
expect() 中正則表達(dá)式的使用 tips
isalive() 的使用 tips
delaybeforesend 的使用 tips
例 1:ftp 的使用
本例實現(xiàn)了如下功能:ftp 登錄到 develperWorks.ibm.com 主機上,并用二進制傳輸模式下載一個名叫 rmall的文件。
清單 1. ftp 的例子代碼
注:
運行后,輸出結(jié)果為:
本例 expect 函數(shù)中的 pattern 使用了 List,并包含了 pexpect.EOF和pexpect.TIMEOUT,這樣出現(xiàn)了超時或者 EOF,不會拋出 expection 。(關(guān)于 expect() 函數(shù)的具體使用,請參閱參考資料)
如果程序運行中間出現(xiàn)了錯誤,如用戶名密碼錯誤,超時或者 EOF,遠(yuǎn)程 server 連接不上,都會使用 c hild.close(force=True) 關(guān)掉 ftp 子程序。調(diào)用 close 可以用來關(guān)閉與子程序的 connection 連接,如果你不僅想關(guān)閉與子程序的連接,還想確保子程序是真的被 terminate 終止了,設(shè)置參數(shù) force=True,其最終會調(diào)用 c hild.kill(signal.SIGKILL) 來殺掉子程序。
例 2:記錄 log
本例實現(xiàn)了如下功能:運行一個命令,并將該命令的運行輸出結(jié)果記錄到 log 文件中 ./command.py [-a] [-c command] {logfilename} -c 后接的是要運行的命令的名字,默認(rèn)是“l(fā)s -l”; logfilename 是記錄命令運行結(jié)果的 log 文件名,默認(rèn)是“command.log”;指定 -a 表示命令的輸出結(jié)果會附加在 logfilename 后,如果 logfilename 之前已經(jīng)存在的話。
清單 2. 記錄 log 的例子代碼
注:
運行:./command.py -a -c who cmd.log
logfile:
只能通過 spawn 類的構(gòu)造函數(shù)指定。在 spawn 類的構(gòu)造函數(shù)通過參數(shù)指定 logfile 時,表示開啟或關(guān)閉 logging 。所有的子程序的 input 和 output 都會被 copy 到指定的 logfile 中。設(shè)置 logfile 為 None 表示停止 logging,默認(rèn)就是停止 logging 。設(shè)置 logfile 為 sys.stdout,會將所有東西 echo 到標(biāo)準(zhǔn)輸出。
logfile_read和logfile_send:
logfile_read:只用來記錄 python 主程序接收到 child 子程序的輸出,有的時候你不想看到寫給 child 的所有東西,只希望看到 child 發(fā)回來的東西。 logfile_send:只用來記錄 python 主程序發(fā)送給 child 子程序的輸入 logfile、logfile_read 和 logfile_send 何時被寫入呢? logfile、logfile_read 和 logfile_send 會在每次寫 write 和 send 操作后被 flush 。
調(diào)用 send 后,才會往 logfile 和 logfile_send 中寫入,sendline/sendcontrol/sendoff/write/writeline 最終都會調(diào)用 send,所以 sendline 后 logfile 中一定有內(nèi)容了,只要此時 logfile 沒有被 close 。
調(diào)用 read_nonblocking 后,才會往 logfile 和 logfile_read 中寫入,expect_loop 會調(diào)用 read_nonblocking,而 expect_exact 和 expect_list 都會調(diào)用 expect_loop,expect 會調(diào)用 expect_list,所以 expect 后 logfile 中一定有內(nèi)容了,只要此時 logfile 沒有被 close 。
如果調(diào)用的函數(shù)最終都沒有調(diào)用 send 或 read_nonblocking,那么 logfile 雖然被分配指定了一個 file,但其最終結(jié)果是:內(nèi)容為空。見下例:
清單 3. log 內(nèi)容為空的例子代碼
運行該腳本后,你會發(fā)現(xiàn)其實 log.txt 是空的,沒有記錄 ls -l 命令的內(nèi)容,原因是沒有調(diào)用 send 或 read_nonblocking,真正的內(nèi)容沒有被 flush 到 log 中。如果在 fout.close() 之前加上 p.expect(pexpect.EOF),log.txt 才會有 ls -l 命令的內(nèi)容。
例 3:ssh 的使用
本例實現(xiàn)了如下功能:ssh 登錄到某個用戶指定的主機上,運行某個用戶指定的命令,并輸出該命令的結(jié)果。
清單 4. ssh 的例子代碼
注:
運行后,輸出結(jié)果為:
使用了 getpass.getpass() 來獲得用戶輸入的密碼,與 raw_input 不同的是,getpass.getpass() 不會將用戶輸入的密碼字符串 echo 回顯到 stdout 上。(更多 python 相關(guān)技術(shù),請參閱參考資料)
例 4:pxssh 的使用
本例實現(xiàn)了如下功能:使用 pexpect 自帶的 pxssh 模塊實現(xiàn) ssh 登錄到某個用戶指定的主機上,運行命令’ uptime ’和’ ls -l ’,并輸出該命令的結(jié)果。
清單 5. 使用 pxssh 的例子代碼
運行后,輸出結(jié)果為:
pxssh 是 pexpect 中 spawn 類的子類,增加了 login, logout 和 prompt 幾個方法,使用其可以輕松實現(xiàn) ssh 連接,而不用自己調(diào)用相對復(fù)雜的 pexpect 的方法來實現(xiàn)。 pxssh 做了很多 tricky 的東西來處理 ssh login 過程中所可能遇到的各種情況。比如:如果這個 session 是第一次 login,pxssh 會自動接受遠(yuǎn)程整數(shù) remote certificate ;如果你已經(jīng)設(shè)置了公鑰認(rèn)證,pxssh 將不會再等待 password 的提示符。(更多 ssh 相關(guān)知識,請參閱參考資料) pxssh 使用 shell 的提示符來同步遠(yuǎn)程主機的輸出,為了使程序更加穩(wěn)定,pxssh 還可以設(shè)置 prompt 為更加唯一的字符串,而不僅僅是“ $ ”和“ # ”。
login?方法
使用原始 original_prompt 來找到 login 后的提示符(這里默認(rèn) original_prompt 是“$”或“#”,但是有時候可能也是別的 prompt,這時就需要在 login 時手動指定這個特殊的 prompt,見上例,有可能是“ > ”),如果找到了,立馬使用更容易匹配的字符串來重置該原始提示符(這是由 pxssh 自己自動做的,通過命令 "PS1='[PEXPECT]\$ '" 重置原始提示符,然后每次 expect 匹配 \[PEXPECT\][\$\#])。原始提示符是很容易被混淆和胡弄的,為了阻止錯誤匹配,最好根據(jù)特定的系統(tǒng),指定更加精確的原始提示符,例如 "Message Of The Day" 。 有些情況是不允許重置原始提示符的,這時就要設(shè)置 auto_prompt_reset 為 False 。而且此時需要手動設(shè)置 PROMPT 域為某個正則表達(dá)式來 match 接下來要出現(xiàn)的新提示符,因為 prompt() 函數(shù)默認(rèn)是 expect 被重置過的 PROMPT 的。
prompt方法
匹配新提示符(不是 original_prompt)。注:這只是匹配提示符,不能匹配別的 string,如果要匹配特殊 string,需直接使用父類 spawn 的 expect 方法。 prompt 方法相當(dāng)于是 expect 方法的一個快捷方法。如果auto_prompt_reset 為 False,這時需要手動設(shè)置 PROMPT 域為某個正則表達(dá)式來 match 接下來要出現(xiàn)的 prompt,因為 prompt() 函數(shù)默認(rèn)是 expect 被重置過的 PROMPT 的。
logout方法
發(fā)送'exit'給遠(yuǎn)程 ssh 主機,如果有 stopped jobs,會發(fā)送'exit'兩次。
例 5:telnet 的使用
本例實現(xiàn)了如下功能:telnet 登錄到某遠(yuǎn)程主機上,輸入命令“l(fā)s -l”后,將子程序的執(zhí)行權(quán)交還給用戶,用戶可以與生成的 telnet 子程序進行交互。
清單 6. telnet 的例子代碼
運行后,輸出結(jié)果為:
interact方法
通常一個 python 主程序通過 pexpect.spawn 啟動一個子程序,一旦該子程序啟動后,python 主程序就可以通過 child.expect 和 child.send/child.sendline 來和子程序通話,python 主程序運行結(jié)束后,子程序也就死了。比如 python 主程序通過 pexpect.spawn 啟動了一個 telnet 子程序,在進行完一系列的 telnet 上的命令操作后,python 主程序運行結(jié)束了,那么該 telnet session(telnet 子程序)也會自動退出。但是如果調(diào)用 child.interact,那么該子程序(python 主程序通過 pexpect.spawn 衍生成的)就可以在運行到 child.interact 時,將子程序的控制權(quán)交給了終端用戶(the human at the keyboard),用戶可以通過鍵盤的輸入來和子程序進行命令交互,管理子程序的生殺大權(quán),用戶的鍵盤輸入 stdin 會被傳給子程序,而且子程序的 stdout 和 stderr 輸出也會被打印出來到終端。 默認(rèn) ctrl + ] 退出 interact() 模式,把子程序的執(zhí)行權(quán)重新交給 python 主程序。參數(shù) escape_character 指定了交互模式的退出字符,例如 child.interact(chr(26)) 接下來就會變成 ctrl + z 退出 interact() 模式。
pexpect 使用 tips
調(diào)試 pexpect 程序的 tips
獲得 pexpect.spawn 對象的字符串 value值,將會給 debug 提供很多有用信息。
清單 7. 打印 pexpect.spawn 對象的字符串 value 值的例子代碼
將子程序的 input 和 output 打 log 到文件中或者直接打 log 到屏幕上也非常有用
清單 8. 記錄 log 的例子代碼
pexpect 不會解釋 shell 中的元字符
pexpect 不會解釋 shell 的元字符,如重定向 redirect,管道 pipe,和通配符 wildcards( “ > ” , “ | ”和“ * ”等 ) 如果想用的話,必須得重新啟動一個新 shell(在 spawn 的參數(shù) command 中是不會解釋他們的,視其為 command string 的一個普通字符)
清單 9. 重新啟動一個 shell 來規(guī)避 pexpect 對元字符的不解釋
如果想在 spawn 出來的新子程序中使用重定向 redirect,管道 pipe,和通配符 wildcards( “ > ” , “ | ”和“ * ”等 ),好像沒有好的方法,只能不使用這些字符,先利用 expect 匹配命令提示符,從而在 before 中可以拿到之前命令的結(jié)果,然后在分析 before 的內(nèi)容達(dá)到使用重定向 redirect, 管道 pipe, 和通配符 wildcards 的目的。
EOF 異常和 TIMEOUT 異常
TIMEOUT 異常
如果子程序沒有在指定的時間內(nèi)生成任何 output,那么 expect() 和 read() 都會產(chǎn)生 TIMEOUT 異常。超時默認(rèn)是 30s,可以在 expect() 和 spawn 構(gòu)造函數(shù)初始化時指定為其它時間,如:
如果你想讓 expect() 和 read() 忽略超時限制,即無限期阻塞住直到有 output 產(chǎn)生,設(shè)置 timeout 參數(shù)為 None。
清單 10. 忽略 timeout 超時限制的例子代碼
EOF 異常
可能會有兩種 EOF 異常被拋出,但是他們除了顯示的信息不同,其實本質(zhì)上是相同的。為了實用的目的,不需要區(qū)分它們,他們只是給了些關(guān)于你的 python 程序到底運行在哪個平臺上的額外信息,這兩個顯示信息是:
有些 UNIX 平臺,當(dāng)你讀取一個處于 EOF 狀態(tài)的文件描述符時,會拋出異常,其他 UNIX 平臺,卻只會靜靜地返回一個空字符串來表明該文件已經(jīng)達(dá)到了狀態(tài)。
使用 run() 來替代某些的 spawn 的使用
pexpect 模塊除了提供 spawn 類以外,還提供了 run() 函數(shù),使用其可以取代一些 spawn 的使用,而且更加簡單明了。
清單 11. 使用 run() 來替代 spawn 的使用的例子代碼
run (command, timeout=-1, withexitstatus=False, events=None, extra_args=None, logfile=None, cwd=None, env=None):
command:執(zhí)行一個命令,然后返回結(jié)果,run() 可以替換 os.system()(更多 os.system() 知識,請參閱參考資料),因為 os.system() 得不到命令輸出的結(jié)果
返回的 output 是個字符串,STDERR 也會包括在 output 中,如果全路徑?jīng)]有被指定,那么 path 會被 search
timeout:單位 s 秒,每隔 timeout 生成一個 pexpect.TIMEOUT 異常
每行之間被 CR/LF (\\r\\n) 相隔,即使在 Unix 平臺上也是 CR/LF,因為 Pexpect 子程序是偽 tty 設(shè)備
withexitstatus:設(shè)置為 True,則返回一個 tuple,里面包括 (command_output, exitstatus),如果其為 False,那么只是僅僅返回 command_output
events:是個 dictionary,里面存放 {pattern:response} 。無論什么時候 pattern 在命令的結(jié)果中出現(xiàn)了,會出現(xiàn)以下動作:
發(fā)送相應(yīng)的 response String 。如果需要回車符“ Enter ”的話,“ \\n ”也必須得出現(xiàn)在 response 字符串中。
response 同樣也可以是個回調(diào)函數(shù),不過該回調(diào)函數(shù)有特殊要求,即它的參數(shù)必須是個 dictionary,該 dictionary 的內(nèi)容是:包含所有在 run() 中定義的局部變量,從而提供了方法可以訪問 run() 函數(shù)中 spawn 生成的子程序和 run() 中定義的其他局部變量,其中 event_count, child, 和 extra_args 最有用。回調(diào)函數(shù)可能返回 True,從而阻止當(dāng)前 run() 繼續(xù)執(zhí)行,否則 run() 會繼續(xù)執(zhí)行直到下一個 event 。回調(diào)函數(shù)也可能返回一個字符串,然后被發(fā)送給子程序。 'extra_args' 不是直接被 run() 使用,它只是提供了一個方法可以通過 run() 來將數(shù)據(jù)傳入到回調(diào)函數(shù)中(其實是通過 run() 定義的局部變量 dictionary 來傳)
清單 12. 其它一些使用 run() 的例子代碼
expect_exact() 的使用
expect_exact(self, pattern_list, timeout = -1, searchwindowsize = -1); expect_exact() 與 expect() 類似,但是 pattern_list 只能是字符串或者是一個字符串的 list,不能是正則表達(dá)式,其匹配速度會快于 expect(),原因有兩個:一是字符串的 search 比正則表達(dá)式的匹配要快,另一個則是可以限制只從輸入緩沖的結(jié)尾來尋找匹配的字符串。還有當(dāng)你覺得每次要 escape 正則表達(dá)式中的特殊字符為普通字符時很煩,那么你也可以使用 expect_exact() 來取代 expect()。
清單 13. expect_exact() 的例子代碼
expect() 中正則表達(dá)式的使用 tips
expect() 中的正則表達(dá)式不是貪婪匹配 greedy match,而是最小匹配,即只匹配緩沖區(qū)中最早出現(xiàn)的第一個字符串。 因為是依次讀取一個字符的 stream 流來判斷是否和正則表達(dá)式所表達(dá)的模式匹配,所以如果參數(shù) pattern 是個 list,而且不止一次匹配,那么緩沖區(qū)中最早出現(xiàn)的第一個匹配的字符串才算數(shù)。
清單 14. expect() 的最小匹配例子代碼
“$”不起任何作用,匹配一行的結(jié)束 (end of line),必須得匹配 CR/LF
正則表達(dá)式中,'$'可以匹配一行的結(jié)束(具體'$'正則表達(dá)式的使用,請參閱參考資料),但是 pexpect 從子程序中一次只讀取一個字符,而且每個字符都好像是一行的結(jié)束一樣,pexpect 不能在子程序的輸出流去預(yù)測。匹配一行結(jié)束的方法必須是匹配 "\r\n" (CR/LF) 。即使是 Unix 系統(tǒng),也是匹配 "\r\n" (CR/LF),因為 pexpect 使用一個 Pseudo-TTY 設(shè)備與子程序通話,所以當(dāng)子程序輸出 "\n" 你仍然會在 python 主程序中看到 "\r\n" 。原因是 TTY 設(shè)備更像 windows 操作系統(tǒng),每一行結(jié)束都有個 "\r\n" (CR/LF) 的組合,當(dāng)你從 TTY 設(shè)備去解釋一個 Unix 的命令時,你會發(fā)現(xiàn)真正的輸出是 "\r\n" (CR/LF),一個 Unix 命令只會寫入一個 linefeed (\n),但是 TTY 設(shè)備驅(qū)動會將其轉(zhuǎn)換成 "\r\n" (CR/LF) 。
清單 15. 匹配一行結(jié)束 1
如果你只是想跳過一個新行,直接 expect('\n') 就可以了,但是如果你想在一行的結(jié)束匹配一個具體的 pattern 時,就必須精確的尋找 (\r),見下例:
清單 16. 匹配一行結(jié)束 2
這個問題其實不只是 pexpect 會有,如果你在一個 stream 流上實施正則表達(dá)式匹配時,都會遇到此問題。正則表達(dá)式需要預(yù)測,stream 流中很難預(yù)測,因為生成這個流的進程可能還沒有結(jié)束,所以你很難知道是否該進程是暫時性的暫停還是已經(jīng)徹底結(jié)束。
當(dāng) '.' 和 '*' 出現(xiàn)在最后時
child.expect ('.+'); 因為是最小匹配,所以只會返回一個字符,而不是一個整個一行(雖然 pexpect 設(shè)置了 re.DOTALL,會匹配一個新行。 child.expect ('.*'); 每次匹配都會成功,但是總是沒有字符返回,因為 '*' 表明前面的字符可以出現(xiàn) 0 次 , 在 pexpect 中,一般來說,任何 '*' 都會盡量少的匹配。
isalive() 的使用 tips
isalive(self)
測試子程序是否還在運行。這個方法是非阻塞的,如果子程序被終止了,那么該方法會去讀取子程序的 exitstatus 或 signalstatus 這兩個域。返回 True 表明子程序好像是在運行,返回 False 表示不再運行。當(dāng)平臺是 Solaris 時,可能需要幾秒鐘才能得到正確的狀態(tài)。 當(dāng)子程序退出后立馬執(zhí)行 isalive() 有時可能會返回 1 (True),這是一個 race condition,原因是子程序已經(jīng)關(guān)閉了其文件描述符,但是在 isalive() 執(zhí)行前還沒有完全的退出。增加一個小小的延時會對 isalive() 的結(jié)果有效性有幫助。
清單 17. isalive() 的例子代碼
delaybeforesend 的使用 tips
spawn 類的域 delaybeforesend 可以幫助克服一些古怪的行為。比如,經(jīng)典的是,當(dāng)一個用戶使用 expect() 期待 "Password:" 提示符時,如果匹配,立馬 sendline() 發(fā)送密碼給子程序,但是這個用戶會看到他們的密碼被 echo back 回顯回來了。這是因為,通常許多應(yīng)用程序都會在打印出 "Password:" 提示符后,立馬關(guān)掉 stdin 的 echo,但是如果你發(fā)送密碼過快,在程序關(guān)掉 stdin 的 echo 之前就發(fā)送密碼出去了,那么該密碼就會被 echo 出來。
清單 18. delaybeforesend 的例子代碼
相關(guān)主題
訪問?Expect?的主頁,了解更多 Expect 的內(nèi)容。
訪問 Python?官方網(wǎng)頁,了解更多 Python 相關(guān)知識。
有關(guān)正則表達(dá)式方面相關(guān)知識,請訪問正則表達(dá)式。
更多 SSH 相關(guān)知識,請訪問?SSH。
更多 os.system() 的介紹,請訪問?os.system()。
在 developerWorks 上查閱所有?Linux 技巧?和?Linux 教程。
參考帖子
總結(jié)
以上是生活随笔為你收集整理的python pp模块_Python模块--Pexpect的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 苏炳添代言!小米12海报曝光:发布会定档
- 下一篇: B站和谐《小林家的龙女仆S》大胸妹 网友