引言
做AP上層應用開發時,需要經常打包、燒寫文件系統,通過終端仿真軟件 SecureCRT 登錄后用串口控制。俗話說,工欲善其事必先利其器,工具軟件與生產力密切相關,值得仔細設置並熟悉之。搜到兩篇不錯的設置文章:SecureCRT 使用技巧,SecureCRT好用的設置。SecureCRT 支持腳本,可以實現一定程度上的自動化操作,把一些常用操作序列封裝成腳本也不錯,目前用的是 SecureCRT 5.1
正文
SecureCRT 腳本的資料網上不多,自帶的幫助文件中 ActiveX Scripting 章有簡要描述,這里概覽下
1. 最常用的 ActiveX script 引擎有 VBScript 和 JScript,都是微軟的東東,所以 SecureCRT 支持這兩種腳本語言。腳本的頭部指定腳本與接口類型,腳本內容以 main 函數作為執行入口
# $language = "VBScript" 或 # $language = "JScript" 或 # $language = "Python"
# $interface = "1.0"
Sub Main 或 function main() { 或 def main():
.....
End Sub } main()
======= 07-20 補充 v6.6 內容 ========
5.1 是 2006 年的老版本了,文檔描述不怎么清楚。目前更新到 6.6 版,從功能到文檔都有了較大完善。幫助里提到 6.6 的 Scripting 加強,包括支持 Python 腳本,和更多的腳本傳參方式,如按鈕和鍵映射。幫助里各內置對象的使用描述更清晰,而且還附加了若干實例,不錯
=====
2. SecureCRT 提供了內置對象與腳本交互,這些對象封裝了屬性與操作,可用來操縱程序行為。其中 crt 是頂級對象,代表了 SecureCRT 進程,其屬性有 ActivePrinter(打印機),Dialog,Screen,Session(會話),Version,Window(窗口) 這些二級對象,操作有 Sleep,Quit
其中,Screen 最為常用,可以用來操作屏幕的輸入輸出,但要注意的是,Screen 只代表客戶區的可見部分,這部分的大小可以通過會話選項里的邏輯行、列數來設定。而整個會話的輸出記錄是回滾區,沒有對應的對象;Dialog 提供了輸入框和 MessageBox
======= v6.6 新增對象 ========
相比 5.1,crt 多了些對象屬性:Arguments(命令行參數),Clipboard(剪貼板),FileTransfer,操作上則添加了 tab 操作(標簽頁屬性)和加強了錯誤處理(處理腳本出錯但繼續執行的情況),GetScriptTab 比較常用,用來獲得執行腳本所在的 tab 對象。其下的二級對象也加了不少屬性和操作
=====
3. Screen 的屬性與操作
屬性:CurrentColumn,CurrentRow(當前光標所在邏輯行),Columns,Rows(可見區中的輸出行數),Synchronous(同步模式,據說為了避免某些情況下的數據丟失,建議開啟)。因為邏輯行、列數限定了可見區的大小,所以當輸出滿屏時, CurrentRow 和 Rows 的值其實是一樣的,都是最大值,不會隨着屏幕輸出再改變,這種設計實在是有些 egg hurt,不知能否通過設置來解決
操作:
Clear 清掉所有輸出,包括回滾與提示符,直接變白板,杯具
Get 獲取某個矩形區域內的輸出文本,用行列數當坐標來指定;Get2 獲取指定兩行間的輸出文本
Send 發送命令字符串(包括換行符); SendSpecial 用來發送 SecureCRT 的一些內置命令,如鍵映射
WaitForString 等待輸出某字符串,支持超時;WaitForStrings 多個字符串中任何一個;WaitForCursor 等待光標移動,WaitForKey 等待任意鍵按下,除了 WaitForStrings,其它均返回布爾值
Print 打印機;ReadString 得到實時原樣輸出?
======= v6.6 描述 ========
object.Get2(row1, col1, row2, col2),和 Get 的參數一個樣。看描述, Get 獲取的的是矩形區域內的文本,空白也被包括,而 Get2 會根據換行 \r\n 來判斷行文本是否已結束,換行也被包括
ReadString 非常有用,可用來獲得命令的執行輸出,慣用法:
1)char = crt.Screen.ReadString() // 無參式,從輸出中一次取一個字符
2)str = crt.Screen.ReadString(StringToWaitFor, TimeOutSeconds) // 超時可省略
3)str = crt.Screen.ReadString(["home", "work"], 10) // [stringarray],類似 WaitForStrings
=====
實例一則
目標:完成文件系統的燒寫,並有簡單的錯誤處理,以免誤操作。這里用的是 JS 腳本
1 #$language = "JScript" 2 #$interface = "1.0" 3 4 var screen = crt.Screen; //創建整屏(可見區)對象引用 5 var dlg = crt.Dialog; 6 var bootloader = "RedBoot> "; //bootloader 提示符 7 var cmdSequ = [ "ip_address -l 10.10.0.3/24 -h 10.10.0.9", 8 "load -r -v -b 0x80100000 rootfs.squashfs", 9 "fis create -b 0x80100000 -e 0x0 -l 0x800000 rootfs"
]; //燒寫命令序列 10 var err_regx = /(error|can't)/i; // (正則表達式對象)錯誤信息匹配,忽略大小寫 11 var err_LH = 2; // 查錯的最小范圍(行高) 12 13 function main() 14 { 15 var upRowL = screen.CurrentRow; //光標所在邏輯行 16 var upColL = screen.CurrentColumn; 17 if (screen.Get(upRowL, 1, screen.CurrentRow, bootloader.length) == bootloader 18 && upColL == (bootloader.length+1)) // 若當前行有提示符且未輸入 19 { 20 for (var i=0; i<cmdSequ.length; ++i) { 21 screen.Send(cmdSequ[i] + "\r"); // 執行操作 22 screen.WaitForString(bootloader); // 等待操作結束 23 upRowL = screen.CurrentRow; 24 var retString = screen.Get2(upRowL-err_LH, upRowL); // 按行獲取輸出文本 25 //dlg.MessageBox(retString); 26 if (retString.search(err_regx) > 0) { // 若輸出文本包含出錯信息 27 dlg.MessageBox("ERROR: " + cmdSequ[i] + "!"); 28 return -1; 29 } 30 } 31 } else { 32 dlg.MessageBox("ERROR: " + bootloader + " not ready!"); 33 } 34 35 return 0; 36 }
號外:(upRowL - err_LH)可能會<=0,執行出錯,但這種情況很少見,就這么將就了吧
本打算通過上次的輸入行和當前提示符之間的輸出來判斷執行是否出錯,但因為 screen ache & hurt 的屬性設計,難以做到。考慮到出錯信息一般都在輸出的尾部,所以這里就偷雞取巧了,只取了最后兩行做判斷
======= 07-20 修正版 ========
1 #$language = "JScript" 2 #$interface = "1.0" 3 4 var g_prompt = "RedBoot> "; //bootloader 提示符 5 var g_cmdSequ = [ "ip_address -l 10.10.0.3/24 -h 10.10.0.23", 6 "load -r -v -b 0x80100000 rootfs.squashfs", 7 "fis create -b 0x80100000 -e 0x0 -l 0x800000 rootfs" 8 ]; //燒寫命令序列 9 var g_errRegx = /(error|can't)/i; // 錯誤信息匹配,忽略大小寫 10 11 var dlg = crt.Dialog; 12 var screen = crt.Screen; 13 14 function main() 15 { 16 var curVersion = getVersion(); 17 if (curVersion >= 6.6) { 18 objTab = crt.GetScriptTab(); 19 screen = objTab.Screen; 20 } 21 screen.Synchronous = true; 22 23 return execCmds(); 24 }
25 /* 獲取軟件版本 */ 26 function getVersion() 27 { 28 return parseFloat(crt.Version); // 6.6 29 } 30 /* 獲取命令輸出 */ 31 function getCmdOutput(command, prompt) 32 { 33 screen.Send(command + '\r'); 34 screen.WaitForString('\r'); // not capture the command issued 35 return screen.ReadString(prompt); 36 } 37 /* 執行命令序列 */ 38 function execCmds() 39 { 40 do { 41 var cursorMV = screen.WaitForCursor(1); 43 } while (cursorMV) // 當光標不再移動
44
45 for (var i=0; i<g_cmdSequ.length; ++i) { 46 var retString = getCmdOutput(g_cmdSequ[i], g_prompt); 47 if (retString.search(g_errRegx) > 0) { 48 dlg.MessageBox("ERROR: " + g_cmdSequ[i] + "!"); 49 return -1; 50 } 51 } 56 57 return 0; 58 }
更新說明:這里使用 readstring 來獲得完整命令輸出,不再根據最后兩個輸出行來判斷;為了保證執行腳本時沒有命令正被執行,這里用了1秒來判斷光標是否在移動,不過我還是覺得老版的判斷方式更穩妥點
-----------------------------
其實 Linux 下還有 expect 命令,可以按照設定的方式與交互式程序進行“會話”,也是不錯的選擇。自動登錄的小例子:
#!/usr/bin/expect -f
set timeout 30
spawn ssh -l username 192.168.1.1
expect "password:"
send "ispass\r"
interact