- 論壇徽章:
- 0
|
如何利用Expect來和Terminal自動交互
在Linux/Unix的世界里,用expect就可以很容易得就可以實現(xiàn)人機(jī)交互的功能,尤其網(wǎng)上好多examples實現(xiàn)比如password, ftp, telnet之類的程序都很簡單。但是如何很好的實現(xiàn)和終端程序交互,尤其是這個終端是用ncurses/curses實現(xiàn),有著豐富的顏色和復(fù)雜的結(jié)構(gòu),這樣的現(xiàn)有的expect就有點力不從心了。現(xiàn)在,筆者參與一個OpenSolaris的項目測試,這個測試要求能夠自動和終端交互,來實現(xiàn)復(fù)雜的自動人機(jī)交互的功能。但是這個項目使用ncurses + python bridge寫成的。為了追求好的用戶體驗,開發(fā)人員使用了很多的屏幕刷新,這就要求expect能購追蹤每一次刷新并且從用戶的最終角度來得到屏幕顯示的是什么。
比如:原來屏幕上顯示的是"Yes or no....", 但是在下一屏刷新的時候,可能之改變了幾個字符,顯示就變成了"Yesterday", 如下所示:
Y e s o r n o ...
Y e s t e r d a y ...
但是在expect_out(buffer)里面,顯示的就是"...Yes or no\r\n...^[[3Gterday", 這樣如果搜尋"Yesterday"這串字符就顯然不可能能夠搜到的。
有人會問"^[[3G"是什么?這個就是termcap/terminfo 用來使terminal具有豐富顯示功能的約定俗成方法。為了方便使用這些功能,人們就把他們組成了各種方法,封裝在curses/ncurses庫中。具體約定的方法請參見附錄1
在expect的創(chuàng)始人Don.Libes的論文〔附錄2〕中, 他提出了一種方法能夠解決上述terminal問題的自動人機(jī)交互,而且作為expect的FAQ給出。但是,他實現(xiàn)的方法是用tcl/tk來模擬一個后臺終端(tk terminal),把顯示的內(nèi)容用控制的方法來實現(xiàn)前臺終端和后臺終端同步顯示,在他封裝的腳本里,用term_expect〔附錄3〕來對后端terminal進(jìn)行監(jiān)控從而間接的對用戶程序顯示的前臺進(jìn)行監(jiān)控。
但是他的方法里面有一個限制,就是前端運行的terminal程序必須能在后端的tk Terminal上正常顯示,但是,據(jù)我所知,不同的terminal控制轉(zhuǎn)義字符是不一樣的,而tk terminal的轉(zhuǎn)移字符遠(yuǎn)遠(yuǎn)不能夠覆蓋所有其他的terminal類型,比如xterm. 而且有很多程序只能在諸如xterm這樣的終端上才能夠正常運行。這就造成了他給出的程序在實際應(yīng)用中受限。更何況很多情況下根本就不允許tk存在(在沒有X的情況下)。
Don.Libes 的例子中也給出了virterm〔附錄4〕作為非tk terminal的解決方案。但這同樣有問題,首先這個程序本身就是一個學(xué)生寫的,用于從他所在大學(xué)的圖書館里自動檢索,而且他模擬的后臺終端是tt終端現(xiàn)以不多見。所以在把virterm直接用于本例也不能夠?qū)崿F(xiàn)。
但是這里的核心思想?yún)s很有用,在后端模仿一個terminal的buffer,來把有用的東西寫到buffer里面。尋找一個定位符(cursor),根據(jù)expect_out(buffer)的內(nèi)容不斷的調(diào)整cursor的位置,不斷進(jìn)行刷新。這樣在buffer里面就能夠保證和前端terminal顯示一致。
首先要根據(jù)控制序列的所在終端調(diào)整完全轉(zhuǎn)義序列的內(nèi)容,使之在expect_out(buffer)一個個的得到處理,重點是根據(jù)內(nèi)容來調(diào)整cursor.在我的prototype的實驗中,如下內(nèi)容可以用于xterm.
expect {
-re "^\r" {
# (cr,) Go to to beginning of line
set cur_col 0
term_cursor_changed
} "^\n" {
# (ind,do) Move cursor down one line
term_down
term_cursor_changed
} "^\b" {
# Backspace nondestructively
incr cur_col -1
term_cursor_changed
} "^\a" {
# Bell, pass back to user
send_user "\a"
} "^\t" {
# Tab, shouldn't happen
send_error "got a tab!?"
} eof {
term_exit
} "^\x1b\\\[K" {
# (ind,do) Move cursor down one line
term_down
term_cursor_changed
} "^\x1b=" {
#???
} "^\x1b(B" {
#
} "^\x1b(0" {
#
} "^\x1b\\\[A" {
# (cuu1,up) Move cursor up one line
incr cur_row -1
term_cursor_changed
} "^\x1b\\\[C" {
# (cuf1,nd) Nondestructive space
incr cur_col
term_cursor_changed
} -re "^\x1b\\\[((\[0-9]{1,})(;*)(\[0-9]{1,}))*r" {
set cur_row [expr $expect_out(2,string)+1]
set cur_col $expect_out(4,string)
term_cursor_changed
} -re "^\x1b\\\[(\[0-9]{1,2})*G" {
# (cuu1,up) Move cursor to the line
set cur_col $expect_out(0,string)
term_cursor_changed
} -re "^\x1b\\\[(\[0-9]{1,2})*m" { # unsupported
} -re "^\x1b\\\[((\[0-9]{1,2})(;*)(\[0-9]{1,2}))*m" { # unsupported
} -re "^\x1b\\\[((\[0-9]{1,2})(;*)(\[0-9]{1,2})(;*)(\[0-9]{1,2}))*m" { # unsupported
# (rmso,se) End standout mode
set term_standout 0
} -re "^\x1b\\\[(\[0-9]{1,})*X" {
} -re "^\x1b\\\[(\[0-9]{1,})*H" {
# (cuu1,up) Move cursor to the line
set cur_row $expect_out(0,string)
term_cursor_changed
} -re "^\x1b\\\[((\[0-9]*);(\[0-9]*))*H" {
# (cup,cm) Move to row y col x
set cur_row [expr $expect_out(2,string)+1]
set cur_col $expect_out(3,string)
term_cursor_changed
} -re "^\x1b\\\[(\[0-9]*)*l" {
} -re "^\x1b\\\[(\[0-9]*)*J" {
# (clear,cl) Clear screen
term_init
term_cursor_changed
} -re "^\x1b\\\[\\\?(\[0-9]{1,})*h" {
# (smkx,ks) start keyboard-transmit mode
# terminfo invokes these when going in/out of graphics mode
set cur_row 1
term_cursor_changed
} -re "^\x1b\\\[\\\?(\[0-9]{1,})*l" {
} -re "^\[\x20-\x7e]+" {
term_insert $expect_out(0,string)
term_cursor_changed
}
在項目的具體實現(xiàn)過程中,由于匹配效率的問題,上述方法被另外以 C庫來寫成。首先我們用expect調(diào)取程序來實現(xiàn)終端交互,并且把expect_out(buffer)的內(nèi)容輸出供C程序來調(diào)取進(jìn)行實施分析。后端的buffer由C程序來進(jìn)行實現(xiàn)。然后expect在根據(jù)這個buffer里面的內(nèi)容能夠來對前端程序進(jìn)行匹配和其他操作。
整體的結(jié)構(gòu)圖如下:
![]()
附錄:1. 控制終端代碼 - Linux 控制終端轉(zhuǎn)義和控制序列 http://www.chinalinuxpub.com/bbs/showthread.php?t=45194
2. Automation and Testing of Character-Graphic Programs
3. term_expect: http://expect.nist.gov/example/term_expect
4. virterm: http://expect.nist.gov/example/virterm
本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u/412/showart_2049636.html |
|