Tera Term TTL(Tera Term Language)是一种专门用于终端自动化的脚本语言,常用于串口、Telnet、SSH等场景中的自动登录、命令发送、设备配置和批量测试。脚本规模较小时,将所有命令直接写在主流程中尚且容易维护,但当自动化任务逐渐复杂后,大量重复代码会让脚本难以阅读和修改。
TTL提供了子过程(Procedure)机制,可以将一组具有独立功能的操作封装起来,然后在主流程或其他过程内部重复调用。掌握proc、call、return等语法,是编写结构清晰、可复用Tera Term TTL脚本的重要基础。
Tera Term TTL子过程是什么
Tera Term TTL中的子过程可以理解为一个具有独立名称的代码块。它通常用于封装某一项重复任务,例如:
-
登录设备;
-
发送一组初始化命令;
-
清理屏幕;
-
执行设备配置;
-
检查命令执行结果;
-
记录测试信息;
-
执行重复性的业务操作。
与简单地复制粘贴命令相比,子过程最大的优势是代码复用。某个操作需要调整时,只需要修改过程本身,所有调用位置都会使用新的逻辑。
典型的TTL子过程结构如下:
ttlproc SendCommand sendln 'show version' wait '#' return
调用时使用:
ttlcall SendCommand
执行到call语句后,TTL会进入对应的子过程;遇到return后,再返回到调用位置继续执行。
TTL子过程的基本定义语法
Tera Term TTL使用proc定义子过程,基本格式如下:
ttlproc 过程名 ; 子过程中的命令 ... return
例如定义一个发送设备状态查询命令的过程:
ttlproc CheckStatus sendln 'show status' wait '#' return
这里的CheckStatus就是过程名称。
调用:
ttlcall CheckStatus
执行流程可以理解为:
主程序 ↓ call CheckStatus ↓ 进入 CheckStatus ↓ 执行 sendln ↓ 执行 wait ↓ return ↓ 回到 call 后面的代码
这种结构特别适合将复杂TTL脚本拆分成多个功能模块。
使用call调用TTL子过程
定义过程之后,需要通过call命令执行。
基本语法:
ttlcall 过程名
例如:
ttlproc LoginDevice sendln 'username' wait 'Password:' sendln '123456' wait '#' return sendln 'login' wait 'Username:' call LoginDevice sendln 'show version'
当执行到:
ttlcall LoginDevice
脚本会进入LoginDevice过程。
过程执行结束后,return将控制权交还给调用者,因此后面的:
ttlsendln 'show version'
仍然会继续执行。
需要注意,call调用的过程通常需要已经被TTL解释器识别。如果脚本中存在多个过程,建议按照清晰的结构组织代码,避免因为过程位置、脚本流程设计不合理而造成维护困难。
子过程参数的定义与传递
TTL子过程不仅可以执行固定命令,还可以接收参数,从而提高代码复用程度。
例如:
ttlproc SendText sendln params return
调用时可以传入参数:
ttlcall SendText 'hello'
子过程中的params可以获取调用时传入的参数。
如果需要传递多个参数,可以按照参数列表进行设计。例如:
ttlproc SendTwoCommands sendln params sendln param2 return
调用:
ttlcall SendTwoCommands 'show version' 'show status'
不过,实际编写参数化TTL过程时,应特别注意参数解析方式和字符串中空格、引号等特殊字符的影响。对于复杂参数,最好先通过简单案例验证TTL版本及具体运行环境的行为。
使用参数提高子过程复用能力
假设需要对多个设备执行相同的配置命令,与其写成:
ttlsendln 'interface eth0' sendln 'description server1' sendln 'interface eth1' sendln 'description server2'
可以考虑把重复逻辑封装为过程。
例如:
ttlproc ConfigInterface sendln 'interface' sendln 'description' return
实际项目中,可以进一步结合参数、字符串拼接以及变量,实现更加灵活的命令生成。
参数化的核心思路是:
固定流程 + 可变化数据
固定的登录、等待、发送、检查流程放在子过程中,可变化的设备名称、接口名称、IP地址等作为参数传入。
这种方式能够明显减少重复代码。
子过程中的变量使用
TTL脚本中的变量可以与子过程配合使用。
例如:
ttlusername = 'admin' password = '123456' proc Login sendln username wait 'Password:' sendln password return
调用:
ttlcall Login
这种方式适合封装登录逻辑。
不过需要区分变量本身与过程参数的使用场景。对于整个脚本都需要使用的配置数据,可以通过变量保存;对于只服务于某一次过程调用的数据,则更适合设计为过程参数。
这样可以降低不同代码模块之间的耦合。
子过程中使用wait进行结果判断
TTL自动化脚本中,一个常见问题是发送命令之后不能立即发送下一条命令,因为设备可能还没有返回结果。
因此子过程经常会将sendln和wait组合起来。
例如:
ttlproc ShowVersion sendln 'show version' wait '#' return
相比单纯发送:
ttlsendln 'show version' sendln 'show status'
加入等待条件之后,脚本可以根据设备返回的提示符继续执行。
对于登录过程,也可以这样组织:
ttlproc Login sendln 'admin' wait 'Password:' sendln '123456' wait '#' return
这样可以把完整的登录动作封装成一个独立模块。
通过子过程封装设备登录
实际自动化项目中,登录通常是最适合抽象成子过程的功能之一。
例如:
ttlproc LoginDevice wait 'login:' sendln 'admin' wait 'Password:' sendln '123456' wait '#' return
主流程只需要:
ttlcall LoginDevice
后续如果登录提示发生变化,例如设备从:
login:
改成:
Username:
只需要修改LoginDevice过程,不需要逐个修改主流程中的登录代码。
多个子过程组合使用
TTL脚本可以定义多个过程,将复杂任务拆成不同功能。
例如:
ttlproc LoginDevice sendln 'admin' wait 'Password:' sendln '123456' wait '#' return proc GetVersion sendln 'show version' wait '#' return proc SaveConfig sendln 'save' wait '#' return
主流程:
ttlcall LoginDevice call GetVersion call SaveConfig
这样的结构比将所有命令连续堆放在一个文件中更加容易理解。
可以按照功能形成类似的结构:
主流程 ├── LoginDevice ├── GetVersion ├── CheckStatus ├── ConfigDevice └── SaveConfig
当脚本规模扩大时,这种模块化思路尤其重要。
子过程中再次调用其他子过程
子过程之间也可以形成调用关系。
例如:
ttlproc LoginDevice sendln 'admin' wait 'Password:' sendln '123456' wait '#' return proc PrepareDevice call LoginDevice sendln 'enable' wait '#' return
主程序:
ttlcall PrepareDevice
执行PrepareDevice时,会进一步调用LoginDevice。
这种方式可以构建多层次的自动化流程,但不建议设计过深的调用链。过程之间的依赖关系越复杂,调试时越难定位问题。一般情况下,让每个过程负责清晰、单一的任务会更容易维护。
return的作用与常见错误
return用于结束当前过程并返回调用位置。
例如:
ttlproc TestConnection sendln 'ping 192.168.1.1' wait '#' return
如果忘记写return,过程结束边界可能无法按照预期工作。
因此推荐保持完整结构:
ttlproc ProcessName ; commands return
不要把主程序代码和过程内容随意交叉排列。
另一个常见问题是误以为return只是普通的流程跳转命令。实际上,在子过程设计中,它承担的是“结束当前过程并返回调用者”的职责。
子过程与goto的区别
TTL同样支持流程跳转,因此有人会使用goto处理重复逻辑,但对于具有明确功能的代码块,过程通常更适合。
例如,下面这种结构:
ttlgoto Login
虽然可以实现流程跳转,但随着代码增加,很容易形成复杂的跳转关系。
相比之下:
ttlcall LoginDevice
能够直接表达“执行登录操作”的意图。
两者可以简单理解为:
| 方式 | 主要用途 |
|---|---|
goto | 在脚本内部进行流程跳转 |
call | 调用一个已经定义的子过程 |
return | 从子过程返回调用位置 |
如果代码需要反复执行一套具有明确功能的操作,优先考虑使用proc + call + return进行封装。
TTL子过程设计中的实用技巧
子过程并不是越多越好。合理拆分才能真正提高脚本质量。
一个过程尽量只负责一个任务
例如:
ttlproc LoginDevice ... return
专门负责登录。
ttlproc BackupConfig ... return
专门负责配置备份。
不要把登录、配置、日志记录和文件处理全部塞进一个超长过程。
给过程使用明确的名称
推荐:
ttlcall LoginDevice call CheckVersion call SaveConfig
不推荐:
ttlcall A call Test1 call DoSomething
过程名称应该尽可能体现实际功能,方便后续维护。
将重复的wait逻辑统一封装
如果多个地方都需要等待设备提示符,可以把相关操作设计为统一流程,避免不同位置出现不一致的等待条件。
避免过程内部依赖过多外部状态
如果一个过程必须依赖大量全局变量才能工作,那么它的复用性会下降。能够通过参数传入的数据,可以优先考虑参数化。
一个较完整的TTL子过程示例
下面的示例展示登录、查询和配置保存等过程如何组合:
ttl; 登录设备 proc LoginDevice sendln 'admin' wait 'Password:' sendln '123456' wait '#' return ; 查询版本 proc CheckVersion sendln 'show version' wait '#' return ; 保存配置 proc SaveConfig sendln 'save' wait '#' return ; 主流程 call LoginDevice call CheckVersion call SaveConfig
实际使用时,应根据目标设备的登录提示符、命令提示符和命令执行结果调整wait条件。
如果设备执行命令后可能出现多种返回结果,还可以结合TTL的条件判断、超时控制等机制进一步增强脚本的可靠性。
常见问题排查
call提示找不到过程
首先检查过程名称是否完全一致,包括大小写和拼写。
例如定义:
ttlproc LoginDevice
调用时却写成:
ttlcall LoginDevce
就会产生问题。
过程执行后没有继续运行
重点检查过程末尾是否正确使用了:
ttlreturn
同时检查过程内部是否存在等待条件一直无法满足的情况。
参数传递结果异常
如果过程需要接收字符串参数,应重点检查调用时的引号、空格以及参数解析方式。复杂字符串不要直接假设解析结果符合预期,最好使用实际TTL环境进行测试。
wait一直等待
很多所谓“子过程调用失败”的问题,实际上发生在过程内部的wait。
例如:
ttlwait '#'
但目标设备实际提示符是:
>
那么脚本会一直等待#,看起来就像call没有返回。
排查时可以先确认设备实际返回内容,再调整等待条件。
Tera Term TTL子过程的推荐组织方式
对于规模较大的自动化脚本,可以按照“主流程 + 功能过程”的思路组织:
ttl; ===== 功能过程 ===== proc LoginDevice ... return proc CheckDevice ... return proc ConfigDevice ... return proc SaveDevice ... return ; ===== 主流程 ===== call LoginDevice call CheckDevice call ConfigDevice call SaveDevice
这种写法能够让主流程非常直观,阅读脚本时可以快速了解整体执行顺序,而具体实现细节则隐藏在对应过程内部。
对于需要长期运行和维护的Tera Term自动化项目,还可以进一步按照登录、设备检测、配置、日志、异常处理等职责划分过程,使TTL脚本逐渐形成类似模块化程序的结构。
掌握proc定义、call调用以及return返回,是Tera Term TTL脚本实现模块化的基础。结合参数、变量、sendln、wait和条件判断后,可以将重复性的终端操作封装成可复用功能,从而减少重复代码,提高脚本的可读性、稳定性和维护效率。