linux - 怎么自動填寫有交互的shell腳本 - SegmentFault
TCL/Expect交互式自動化測試概要 - - ITeye技術網站
expect是一種基於TCL,能與交互式程序進行“可程序化”會話的腳本語言,是一種可以提供“分支和嵌套結構”來引導程序流程的解釋型腳本語言。
expect由一系列expect-send對組成:expect等待輸出中輸出特定的字符,然后發送特定的響應。
安裝expect:sudo apt-get install expect
expect相關軟件包版本有多個,如expect-tcl8.3、expectk、expect-dev等,可根據自身需求選擇安裝。
(文中涉及的例子請參考 http://lhq1013.iteye.com/blog/907759)
一、示例:
#!/usr/bin/expect
spawn sudo -s
expect "password: "
send "vmkid\r"
expect "~# "
expect eof
解釋:首行“#!“聲明此為expect腳本,具有可執行權限,路徑需正確指明Expect解釋程序的位置。
“spawn"該命令用來來啟動腳本和命令的會話,這里啟動的是sudo命令,實際上命令是以衍生子進程的方式來運行的。
“expect"對執行command后的輸出進行匹配檢測,可使用正則表達式和通配符等。
“send”用例發送command。
“expect eof"檢測到文件結尾,退出。
二、語法與需注意的點
腳本都有相似之處,Expect是基於TCL的,有以下幾個地方需要注意:
1)“{”與前面的字符必須空一格,否則會被解釋器認為是與前面字符是同一個詞。
2)一條命令結尾的“;”是可有可無的,但如果同一行寫來多條命令,則必須以“;”分隔,否則會被誤認為只有一條命令。
3)expect檢測的雙引號中的字符串,若有多行,除前引號后面的那行外,其余的均需要頂格寫,否則空格也會被匹配進去,或者前面用通配符“*”來匹配也行。
4)如果發現腳本掛在了某個點上,可以試着在前一個send前面增加一小會兒sleep時間。因為在提到提示后,一系列的程序(rn, ksh,zsh,telnet,etc.)和設備拋棄或忽略的按鍵等響應的“太快”了。
5)某些程序每次產生的結果都是不一樣的,此時最好用通配符來匹配。
三、流程控制
expect既然是“分支和嵌套結構”的,那么它必須提供相應的功能。除了基於TCL的if/else等條件判斷外,其自身也提供來expect流程控制的功能,示例如下:
send "sudo -s"
expect {"password: " {send "vmkid\r"; exp_continue}
"~# " {puts "------break----"}
}
意思是發送“sudo -s”命令后,有兩種可能的輸出,如果輸出為“password:“,則執行后面所跟的”{}“中的命令,發送密碼並跳出此次循環,直到輸出結果為”~# “才跳出整個expect循環。另外,expect與if/else等也可循環嵌套使用。
四、過程
某些代碼有時是需要重復操作的,比如手機在某些特定的情況下可能需要反復重啟等,此時我們可以將其寫在某一個過程中,直接調用該過程,以減少和簡化代碼。
proc restartPhone {x} {
if {$x == 1} {
spawn adb shell
expect "~# "
send "reboot\r"
expect "*"
} else {
}
}
如上,在需求重啟手機時,我們只要調用”restartPhone 1"便可以了。
五、list
測試過程中,往往需要順序執行多個測試計划,逐一寫的話,用例一多就較為麻煩且不便於管理,在java中對此我們常用for循環讀取數組等方式來進行,tcl也可以,但tcl中用數組相較於java來說稍顯麻煩,需要分別指定數組下標進行賦值,數組大小與下標沒有必然關系,故本人選擇了list來協作for來完成這一任務。示例如下:
set a {Java VM Performance Android}
for {set i [expr [llength $a] - 1]} {$i >= 0} {incr i -1} {
set b "start --plan "
lappend b [lindex $a $i]
send $b
send "\r"
expect "Test summary:*pass*fail*timeOut*omitted*notExecuted*Total"
sleep 10
}
解釋:set arg value:將“{}”中的所有值賦給a,a是列表變量,列表中的每個值以空格隔開。
$a:變量置換,若a在之前以及被賦值,則可以用“$“加上變量名來調用。
incr:遞增,上例中意思為每循環一次,就對變量i遞增(-1)。
set i [expr 1+2+$x]:命令置換,由[]括起來的TCL命令及參數,會倒置某一命令的所有或部分單詞被另一個命令的結構代替,可嵌套使用。如:若x的值為3,則輸出結果為6。
lappend varname value? value……?:將value作為一個元素附加到變量varname后面:如:set a 1;lappend a 2,其輸出結果為12。
lindex list index:返回list的第index個元素。如:lindex {10 9 8 7 6} 2,其返回結果為8。
sleep 10:睡眠10秒鍾。
六、timeout
expect的timeout時間, 是以秒為單位, 如果設置為0, 是根本就不等待, 設置為-1, 是永遠等待.
set timeout 30
七、match_max
expect patlist1 action1 patlist2 action2.....
該命令一直等到當前進程的輸出和以上的某一個模式相匹配,或者等到時間超過一個特定的時間長度,或者等到遇到了文件的結束為止
每一個patlist都由一個模式或者模式的表(lists)組成。如果有一個模式匹配成功,相應的action就被執行。執行的結果從expect返回。被精確匹配的字符串(或者當超時發生時,已經讀取但未進行匹配的字符串)被存貯在變量expect_match里面。模式必須匹配當前進程的從上一個expect或者interact開始的所有輸出(所以統配符*使用的非常)的普遍。但是,一旦輸出超過2000個字節(默認值),前面的字符就會被忘記,這可以通過設定match_max的值來改變。
set match_max 3500
八、參數
1)#!/usr/bin/expect -f
-f 參數指定從哪個文件中讀取命令。當被用在#!指示(見上)中時此參數是可選的,所以其它參數可在命令行中提供。
-i 參數使expect交互地提示輸入命令,而不是從文件中讀命令。命令提示行通過exit命令或一個eof字符結束。
2)expect [[-opts] pat1 body1] ... [-opts] patn [bodyn]
-re 強制string按regexp模式解釋。
-nocase 使輸出中的大寫字符也按小寫字符匹配。
更多參數的使用,參見man expect。
九、autoexpect
autoexpect是Expect的一個工具,可以幫助你很快地生成script。但缺點是自動生成的腳本通用性不強,采用的是完全匹配,需要自行做修改,且布局也比較凌亂,最好也是手工修改下。
安裝:sudo apt-get install expect-dev
Expect開發版本5.44.1.15-1,此版本中已經包含了autoexpect這一工具,較低的版本可能不包含此功能,可在系統——>系統管理——>新立得軟件包管理器中查看相關信息。
啟動autoexpect的方式大致有如下幾種:
1)終端輸入“autoexpect”回車啟動並開始錄制,默認會在當前目錄下產生名為script.exp的腳本,
2)autoexpect后面直接跟命令語句啟動
如autoexpect ssh tester@10.5.176.86
3)啟動時指定腳本的名字
autoexpect -f name.exp
退出錄制只需在終端輸入“exit”回車即可