Files
ETTerms/docs/ttl-script-reference.md
etwenandClaude Opus 4.8 fae5ca7337 fix(ttl): add sendlnretry — resend until the device confirms it ran the command
A device that is still booting can silently discard console input the moment its
shell takes over the tty (tty reopen / termios flush). The line sent by `sendln`
vanishes — the device neither echoes nor runs it — so the following `wait` blocks
forever and the rig sits dead.

This is a race, not a delay: `pause` before `sendln` only lowers the odds of hitting
the window, it can never close it. Evidence from a 45-cycle overnight power-cycle run
(For_AI/[COM121]_20260715): 3 cycles hung this way, and the swallowed sends landed at
the same DUT uptime (31.8-34.8s) as the 42 that worked. The `random: crng init done`
line those hangs share is a symptom, not the cause — it only appears because a hung
script stops power-cycling, letting the DUT reach uptime 89.7s it never otherwise sees.

Single-string `wait` aborts the script on timeout (deliberate, see CLAUDE.md), so the
TeraTerm idiom `wait` -> `if result = 0 then goto retry` cannot express a resend here.
Hence a new command rather than a semantics change:

    sendlnretry '<text>' '<confirm keyword>' [max attempts]

Sends, then waits for proof the device actually ran it, and resends if that proof does
not arrive. Attempts omitted = retry until it gets through. On success result=1; when
attempts run out result=0 and the script continues so it can handle the failure.

Semantics: clears the receive buffer before each send (a match can only come from this
send); on a hit consumes only up to the first occurrence, leaving the rest for the next
`wait`; deliberately no settle (the keyword appearing is itself proof). Per-attempt
timeout follows timeout/mtimeout when set, else 3s — unlike `wait`, 0 cannot mean
"forever" here since that would mean never retrying.

Not yet run against hardware: compiles clean (cross-built win-x64 on Linux), but the
DUT power-cycle rig is the first real execution.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MKF7C75bhA4ArQhyMpnsxU
2026-07-16 09:22:49 +08:00

16 KiB
Raw Permalink Blame History

TTL Script Reference — ETTerms

ETTerms 的 TTLTera Term Language)腳本引擎移植自 MyTeraTerm,改為驅動原生 ISessionChannelSSH / Serial / Shell 皆可)。在分頁上按 ▶ Script 載入 .ttl 對該分頁執行;或用 toolbar 的 ▶ Run All / ▶ Group1-3 批次執行。

v0.5.0 起大幅對齊 TeraTerm macro 指令集。 本文前段是 ETTerms 獨有指令後段是與 TeraTerm 共有的指令

執行於背景執行緒,可隨時按 Stop 中止;wait / pause 期間皆可取消。

腳本的 trace 訊息([wait] 進度、>> 送出回顯、錯誤)會以灰色顯示在該分頁的終端機裡, 可由 Settings → Terminal → Show script trace in terminal 關閉;dispstr 是腳本明確要 顯示的內容,一律顯示。這些灰色訊息只出現在畫面上——不會送到裝置、不會寫進 ⏺ Log 側錄檔、AI serial bridge 也讀不到,機台原始 log 保持乾淨。


語法規則

  • 一行一個指令;前後空白會被去除。
  • 註解:; 之後到行尾(引號內的 ; 不算)。
  • 字串引號 '...'"..." 皆可。
  • Label:名稱 一行,供 goto / call 跳轉。
  • 變數以 名稱 = 運算式 指派;名稱須符合 [a-zA-Z_][a-zA-Z0-9_]*,大小寫不分。
  • 運算式支援:整數(十進位 / 0x1F / $1F)、字串、括號、+ - * / %、 比較 = == <> != > < >= <=、邏輯 and or xor not(同義 && || !)。 比較時兩邊都是數字用數值比較,否則字串比較;未定義變數視為 0
  • 單行 ifif <條件> <指令>(如 if result = 0 goto retry);區塊 if 用 then ... endif

系統變數

變數 說明
result 多數指令的結果碼(各指令說明)。
inputstr waitln / recvln / inputbox / sprintf 的結果字串。
matchstr waitregex / strmatch 命中的整段文字。
groupmatchstr19 regex 群組(waitregex / strmatch)或 strsplit 的分段。
timeout wait 家族的逾時(秒),timeout = 100 = 無限等待。
mtimeout 逾時的毫秒部分,與 timeout 相加(mtimeout = 500)。

一、ETTerms 獨有指令

送出並確認(sendlnretry

指令 語法 說明
sendlnretry sendlnretry '文字' '確認關鍵字' [最多送出次數] 送出 文字 + \r\n,然後等 確認關鍵字;沒等到就重送。次數省略 = 一直重送到收到為止。命中 result=1;用完次數 result=0繼續執行(不中止腳本)。

為什麼需要它:裝置在開機、console 剛被 shell 接手的瞬間,可能把收到的輸入直接丟掉 (tty 重開 / termios flush)。此時 sendln 送出的整行會無聲無息地消失——裝置不會回顯、 也不會執行,後面的 wait 就永遠等不到,腳本整個卡死。這是機率性的: 在 sendln 前面加 pause 只是降低撞上那個窗口的機率,不可能根治sendlnretry 的作法是送完就確認對方真的有反應,沒反應就再送一次。

; 開機後下 tpm2,沒跑到就自動重送(無限重送)
wait 'root@(none):/#'
sendlnretry 'tpm2' 'TPM 2p0'
wait 'PASS'

; 最多送 3 次,還是不行就自己處置
sendlnretry 'tpm2' 'TPM 2p0' 3
if result = 0 then
    dispstr 'tpm2 送了 3 次都沒反應'
endif

使用要點:

  • 確認關鍵字要挑「命令真的有跑」才會出現的輸出(例:TPM 2p0), 不要挑指令回顯(例:tpm2)——tty 的 echo 由核心產生,不保證命令有被 shell 讀走。
  • 每次送出前會清空接收緩衝,確保比對到的是「這次送出」的回應而不是殘留輸出。 命中後只消費到第一次出現處,後面的輸出留在緩衝裡給接下來的 wait 用 (所以上例的 wait 'PASS' 照常會等到)。
  • 每次送出後等確認的逾時:timeout/mtimeout 有設就用設定值,沒設預設 3 秒 (注意這與 wait 不同——wait 的 0 是無限等,但無限等在這裡等於永不重送)。
  • 指令最好是可重複執行的:若逾時設得太短、而裝置其實只是回應慢,會造成同一個指令送兩次。

Group 同步(多分頁協同)

以下指令只能在 Run Group 模式下使用(toolbar 的 ▶ Group1-3)。 ▶ Script▶ Run All 會拒絕含這些指令的腳本。 Group 內的成員依序編為 A, B, C...(顯示在 cell footer,如 [Group1-A])。

⚠ 刻意設計:group 同步時一個成員停止(Stop / 逾時 / 錯誤),其他成員會停在 barrier 等——整組一起停下來,避免半組繼續跑造成狀態不一致。

指令 語法 說明
waitall waitall '字串' 各成員各自 wait 到字串出現,再等其他成員到齊才繼續。
sendlnall sendlnall '文字' 等所有成員到達此行後,每人各自 sendln 同一段文字。
sendlngroup sendlngroup A '文字' 只有指定 memberA/B/C...sendln,其他成員跳過。
; Group1  A=SW1, B=SW2, C=SW3  同步升級
waitall '#'
sendlngroup A 'copy tftp://10.0.0.1/sw1.bin flash:'
sendlngroup B 'copy tftp://10.0.0.1/sw2.bin flash:'
sendlngroup C 'copy tftp://10.0.0.1/sw3.bin flash:'
waitall '#'
sendlnall 'reload'

PDU 電源控制(iPoMan II/IIISNMP

指令 語法 說明
pduconnect pduconnect <device> <ip> 連 PDU 並驗證,result 1/0。device 為自訂編號。
pductrl pductrl <device> <port> <0|1> 指定插座 OFF(0)/ON(1)result 1/0。
pduconnect 1 192.168.1.21
if result = 0 goto fail
pductrl 1 3 0        ; DUT 斷電
pause 5
pductrl 1 3 1        ; DUT 上電
wait 'login:'
:fail

行為與 TeraTerm 不同之處(重要)

項目 ETTerms 行為
wait單字串 命中後需裝置安靜 300ms(無新資料)才接受,並取「最後一次」出現——排除輸出中途的指令回顯(如 SVOS> help)造成腳本搶跑。逾時會中止腳本TeraTerm 是 result=0 繼續),因此 TeraTerm 常見的 waitif result = 0 then goto retry 重送寫法在單字串 wait 上做不到;要「送出後確認、沒回應就重送」請用 sendlnretry
wait多字串 TeraTerm 相容:任一命中即繼續,result = 第幾個字串(1 起算);逾時 result=0 繼續執行、無 settle。
goto / 跨區塊跳轉 goto 跳出 if/while 區塊後,該區塊的迴圈控制即結束(同層繼續直行)。避免 goto 跳「進」區塊中間。
call 可以在迴圈 / if 內使用(行內執行,返回後迴圈續跑)。
include 路徑須為絕對路徑,或相對於 ETTerms 的工作目錄。
浮點數 TTL 無浮點型別;sprintf/sprintf2 的浮點引數以字串傳入('3.14')。

二、與 TeraTerm 共有指令

通訊

指令 語法 說明
send send '文字' 送出文字(不加換行)。
sendln sendln '文字' 送出文字 + \r\n
sendfile sendfile '路徑' 把整個檔案內容原樣送出(result 1/0)。
wait wait '字串' ['字串2' ...] 等待字串出現(行為差異見上表)。
waitln waitln '字串' ['字串2' ...] 包含任一字串的完整一行;該行存入 inputstrresult=第幾個;逾時 0。
waitregex waitregex '樣式' ['樣式2' ...] 等 regex 命中;matchstr / groupmatchstr1-9 設定,result=第幾個;逾時 0。
recvln recvln 收下一行到 inputstrresult 1 成功 / 0 逾時。
flushrecv flushrecv 清空接收緩衝。
dispstr dispstr '文字' [更多...] 在終端機顯示訊息(灰色;不送裝置、不進側錄 log、不受 Show script trace 開關影響)。
sendbreak sendbreak 送 serial breakSerial 限定,約 300ms)。
setbaud setbaud 115200 執行中改 baud rateSerial 限定)。
setdtr / setrts setdtr 1 / setrts 0 控制 DTR / RTS 腳位(Serial 限定)。

控制流

指令 語法 說明
if if <條件> <指令>if <條件> then ... [elseif] [else] endif 單行或區塊,可巢狀。
while / endwhile while <條件> 條件為真時重複。
until / enduntil until <條件> 條件為時重複。
for / next for i 1 10 i 從 1 到 10(含),自動 ±1。
do / loop do [while|until <條件>] ... loop [while|until <條件>] 前測或後測迴圈;都不帶條件時為無限迴圈(用 break 離開)。
break / continue 跳出 / 提前進入下一輪(while / until / for / do 皆可)。
goto goto 標籤 跳到 :標籤
call / return call 標籤 呼叫副程式(:標籤 起,return 返回;可在迴圈內用)。
include include '檔案.ttl' 執行另一個腳本檔(可巢狀 8 層;檔內 exit 只離開該檔)。
pause pause 秒 暫停(可 Stop 取消)。
mpause mpause 毫秒 毫秒級暫停。
end end 結束腳本(成功)。
exit exit include 檔內=離開該檔;主檔=同 end
timeout / mtimeout timeout = 10 wait 家族逾時(秒 / 毫秒,兩者相加)。

字串

指令 語法 說明
strlen strlen <str> result = 長度。
strcompare strcompare <s1> <s2> result = -1 / 0 / 1。
strconcat strconcat <strvar> <str> strvar += str。
strcopy strcopy <str> <pos> <len> <strvar> 取子字串(pos 1 起算)。
strinsert / strremove strinsert <strvar> <pos> <str> / strremove <strvar> <pos> <len> 插入 / 刪除。
strscan strscan <str> <substr> result = 位置(1 起算,0 = 沒找到)。
strmatch strmatch <str> <regex> regex 比對:result = 位置,matchstr / groupmatchstr1-9
strreplace strreplace <strvar> <pos> <regex> <newstr> 自 pos 起 regex 全部取代,result 1/0。
strtrim strtrim <strvar> ['字元集'] 去頭尾字元(預設空白)。
strsplit strsplit <str> <sep> [count] 切成 groupmatchstr1-9result = 個數。
strjoin strjoin <strvar> <sep> [count] groupmatchstr1..count 接回一串。
tolower / toupper tolower <strvar> <str> 轉小寫 / 大寫。
str2int / int2str str2int <intvar> <str> / int2str <strvar> <int> 字串 ↔ 整數(str2int 的 result 1/0)。
code2str / str2code code2str <strvar> 65 / str2code <intvar> 'A' 字元碼 ↔ 字元。
sprintf sprintf <格式> [引數...] C printf 格式化 → inputstr
sprintf2 sprintf2 <strvar> <格式> [引數...] 同上但存到指定變數(詳見下節)。
expandenv expandenv <strvar> '%USERPROFILE%\x' 展開環境變數。

檔案 / 資料夾

指令 語法 說明
fileopen fileopen <fhvar> <路徑> <append 0|1> [readonly 0|1] 開檔;fh 存入變數(-1 失敗)。readonly=1 開來讀。
filecreate filecreate <fhvar> <路徑> 建新檔(覆寫)供寫入。
filereadln filereadln <fh> <strvar> 讀一行;result 1 = EOF、0 = 成功(TeraTerm 相容)。
filewrite / filewriteln filewrite <fh> <str> 寫入(ln 版加換行)。
fileclose fileclose <fh> 關檔(腳本結束會自動關)。
filedelete filedelete <路徑> 刪檔,result 1/0。
filesearch filesearch <路徑> result 1 = 檔案存在。
basename / dirname basename <strvar> <路徑> 取檔名 / 取目錄。
makepath makepath <strvar> <dir> <file> 合成路徑。
foldercreate / folderdelete / foldersearch foldercreate <路徑> 建 / 刪(空)/ 查資料夾。
getdir / setdir getdir <strvar> / setdir <路徑> 取得 / 變更工作目錄。
logopen / logwrite / logclose logopen '檔名' 腳本專屬 log 檔(sendlogwrite 會寫入)。

對話框 / 雜項

指令 語法 說明
messagebox messagebox '訊息' ['標題'] 訊息框。
inputbox inputbox '提示' ['標題'] [預設值] 輸入框 → inputstr(取消 result=0)。
yesnobox yesnobox '訊息' ['標題'] Yes/No → result 1/0。
beep beep 系統提示音。
getdate / gettime getdate <strvar> ['%Y%m%d'] 日期 / 時間字串(strftime 子集:%Y %y %m %d %H %M %S %j %a %A %b)。
getenv / setenv getenv 'PATH' <strvar> 讀 / 寫環境變數(行程內)。
random random <intvar> <max> 0〜max(含)亂數。
exec exec '記事本.exe 檔案' ['show'|'hide'] [wait 0|1] 啟動外部程式;wait=1 時 result = exit code。
getver getver <strvar> ETTerms 版本字串。
getttdir getttdir <strvar> ETTerms 執行檔目錄。
uptime uptime <intvar> 系統開機至今毫秒數。
ifdefined ifdefined <var> result:0 未定義 / 1 整數 / 2 字串。
clipb2var / var2clipb clipb2var <strvar> / var2clipb <str> 剪貼簿 ↔ 變數。
crc32 crc32 <intvar> <str> CRC-32。
checksum8/16/32 checksum8 <intvar> <str> byte 加總(8/16/32 bit)。

未支援(節錄)

檔案傳輸協定(xmodem* / zmodem* / kmt* / scp*)、連線管理(connect / disconnect / closett)、密碼系列(getpassword 等——ETTerms 密碼一律走 Windows Credential Manager)、 陣列(intdim / strdim)、waitevent / waitn / setecho / settitle / listbox / statusbox


sprintf2 格式化

sprintf2 變數 格式字串 [引數 ...] 以 C printf 規則格式化,與 Tera Term 一致。

sprintf2 ver 'Tera Term 4.%d' 51                  ; ver = "Tera Term 4.51"
sprintf2 test '%s=%d %s=0x%x' 'dec' 10 'hex' 33   ; test = "dec=10 hex=0x21"
  • 轉換型別c d i o u x X e E f g G a A s旗標- + 0 # 與空白;寬度/精度支援 *
  • 浮點數以字串傳入:sprintf2 s '%.2f' '3.14159'
  • 格式字串不展開變數(保持字面值),引數會展開,故 sprintf2 s '%s,' s 可累加自身
  • result:0 成功、1 缺格式、2 格式無效、3 引數無效、4 目的變數無效

範例

自動登入 + 失敗重試(goto / 單行 if)

timeout = 15
retry = 0

:login
sendln ''
wait 'login:'
sendln 'admin'
wait 'Password:'
sendln 'secret'
wait 'Login incorrect' '$'
if result = 2 goto ok        ;  2 個字串($ 提示)命中  成功
retry = retry + 1
if retry < 3 goto login
messagebox '登入失敗 3 次' 'Login'
end

:ok
dispstr 'login ok'

waitln + strmatch 解析輸出

sendln 'show environment'
timeout = 10
waitln 'Temperature'                       ; 例如 "Temperature: 47 C"
if result = 0 goto notfound
strmatch inputstr 'Temperature:\s*(\d+)'
if result > 0 then
    str2int temp groupmatchstr1
    if temp > 60 then
        messagebox '過溫!' 'ALERT'
    endif
endif
:notfound

for 迴圈 + 檔案輸出

getdate today '%Y%m%d'
sprintf 'report_%s.txt' today
filecreate fh inputstr

for i 1 5
    sendln 'cat /proc/loadavg'
    recvln                       ; 回顯
    recvln                       ; 資料行
    filewriteln fh inputstr
    pause 2
next
fileclose fh

call 副程式(迴圈內可用)

for i 1 3
    call powercycle
    wait 'login:'
next
end

:powercycle
pductrl 1 3 0
pause 5
pductrl 1 3 1
return

do-loop 等裝置就緒

; 注意:單字串 wait 逾時會「中止腳本」,輪詢請用 waitln(逾時 result=0 繼續)
timeout = 2
do
    sendln ''
    waitln 'SVOS>'
loop until result = 1      ;  2 秒敲一次直到提示出現