先说说我为什么写这篇东西。前一阵帮客户做一批路由器的配置巡检,三十多台设备,每台都要登录、看版本、查接口、记录状态。刚开始我老老实实一台一台敲,敲到第十台手指头就开始罢工了,复制粘贴还担心漏行。后来我花了一个下午,把重复操作写成了 SecureCRT 自动化脚本,之后每天巡检就是双击脚本、等汇总、看报告的事。这种改变,如果你也在和网络设备、服务器终端打交道,值得了解一下。
SecureCRT 本身是一个老牌的终端模拟工具,大家通常用它来登录路由器、交换机、服务器,好处是会话管理、标签页、日志保存都很成熟。但大多数人只用了它三分之一的能力。它内置的脚本自动化能力,能帮我把“登录之后反复敲命令”这件事变成程序自动完成。这篇文章不是讲理论,而是我从选脚本语言、写第一个能跑的脚本,到批量巡检、配置备份、关键字告警这几个实战场景的完整记录。
1. 为什么给SecureCRT写自动化脚本
1.1 一个让人头疼的“手工活”
网络设备运维里最常见的工作就是“巡检”。每天上班,打开 SecureCRT,点开 R1 的标签页,等提示符出现,敲一条show version,等输出,看一眼版本,再敲show ip interface brief,看接口状态,最后把关键信息复制到 Excel 里。一台设备五分钟,十台就是一小时,而且整个过程毫无技术含量,唯一考验的是耐心和手速。
我见过很多同事把命令做成“记事本模板”,登录设备后一行行复制粘贴。说实话,这比纯手工快一些,但隐患很大:粘贴多一个回车、少一个空格,命令就错了;设备提示符没到位就把命令粘贴过去,输出乱七八糟;更别提几十台设备,复制粘贴到后面根本不知道粘贴给谁了。
SecureCRT 脚本正好解决这个问题。它的核心逻辑很简单:脚本替你管理“发什么命令、等待什么回应、把输出放在哪里”。你不需要改变登录习惯,不需要换成别的自动化工具,就用已经在用的 SecureCRT,把重复劳动交给程序。
1.2 脚本化选哪个语言更省心
SecureCRT 的脚本系统基于 ActiveX 自动化接口,脚本里通过一个叫crt的根对象来操作程序。支持的语言有好几种,最常见的是 VBScript 和 Python,另外也支持 JScript、Perl。我个人的习惯是:简单脚本用 VBScript,因为它是 Windows 系统内置的脚本引擎,SecureCRT 装完就能跑,不需要额外安装解释器,文件名保存为.vbs即可;稍微复杂的批量处理、文本匹配、报表输出,我用 Python,因为文件处理和字符串操作方便得多。
| 脚本语言 | 运行依赖 | 适合场景 | 我的使用建议 |
|---|---|---|---|
| VBScript | SecureCRT 内置 | 发送命令、等待提示符、日志开关、简单循环 | 日常巡检首选,零依赖 |
| JScript | SecureCRT 内置 | 语法更适合 JavaScript 用户 | 除非你用 JS 很熟,否则不如 VBScript |
| Python | 需配置解释器或内置运行时 | 复杂文本解析、批量报表、运维自动化整合 | 做批量分析时推荐 |
| Perl | 需自行准备环境 | 少数老运维环境 | 新项目不推荐 |
不管你选哪种语言,核心对象就那么几个。crt.Screen负责向终端发送命令、等待某个字符串出现;crt.Session负责连接、断开、日志记录;crt.Dialog负责弹窗提示、简单输入。后面所有代码都是围绕这三个对象展开。
顺便说明一下范围:脚本自动化适合用在你有权限维护的设备上,比如公司内部网络设备、测试环境、模拟器。做任何批量操作之前,先确认这台设备允许你执行这些命令,不要在未授权的系统上跑脚本,这个底线我一直守着。
2. 上手第一步:脚本引擎与运行环境
2.1 脚本文件写在哪、怎么运行
在 SecureCRT 里运行脚本不需要额外插件。先创建一个文本文件,把后缀改成.vbs或者.py,然后在 SecureCRT 顶部菜单找到Script,选择Run,选中你的脚本文件,就能在当前活动标签页里执行。
我建议把脚本统一放到一个固定目录,比如E:\auto\,不要散落在桌面和下载文件夹。原因很简单:脚本里经常要读写外部文件,绝对路径写死了之后,换台电脑只需要改一处,找起来也方便。
文件编码是个容易踩坑的点。如果脚本里只有英文,随便什么编码都行;一旦出现中文提示、中文输出,就要注意保存编码。VBScript 在中文 Windows 下一般建议保持 ANSI 编码,或者使用 UTF-8 with BOM,避免Dialog.MessageBox弹出乱码。Python 脚本则统一用 UTF-8,文件头部可以加一行编码声明,实际执行时以编辑器保存的编码为准。
2.2 最小可运行的“Hello 设备”脚本
先写一个最简单的 VBScript 脚本,目标很朴素:在已经连上设备的终端里,发送一条show version,等命令结果回来后弹一个提示框。假设设备的命令行提示符是#,如果你的是>或者$,把下面代码里的#替换成你的实际提示符。
Sub Main() ' 先按回车,确保屏幕上处在提示符状态 crt.Screen.Send vbCr crt.Screen.WaitForString "#", 10 ' 发送 show version 命令 crt.Screen.Send "show version" & vbCr crt.Screen.WaitForString "#", 10 crt.Dialog.MessageBox "show version 命令已执行,请查看屏幕输出。" End Sub这段代码里最关键的是crt.Screen.WaitForString "#", 10。它的含义是:让脚本卡在这里,最多等 10 秒,直到终端屏幕上出现#这个字符串。等到了,往下走;等不到,超时后继续下一行。
这里有一个非常重要的思维转变:不要用固定延时去等设备响应。我见过很多人写crt.Sleep(5000),睡五秒再发下一条命令。这在实验室里可能没问题,但生产环境网络一抖动,五秒不够,设备没反应过来,命令就发出去了;网络顺畅时,五秒又浪费了。正确的做法永远是“等一个明确的提示符”,就像人在终端前操作一样:看到#才继续敲命令。
crt.Screen.Send "命令" & vbCr里的vbCr是回车符,在 VBScript 里也可以用Chr(13)表示。不要用vbLf,很多网络设备只认回车,不认换行。
2.3 Python 脚本的写法差异
如果你的 SecureCRT 版本支持 Python 脚本(8.0 之后的绝大多数版本都支持,具体看你安装时是否勾选了对应组件),同样功能用 Python 写是这样:
import SecureCRT crt = SecureCRT.ApplicationFactory() crt.Screen.Send("\r") crt.Screen.WaitForString("#", 10) crt.Screen.Send("show version\r") crt.Screen.WaitForString("#", 10)Python 版的SecureCRT.ApplicationFactory()是脚本宿主的入口,拿到crt对象之后,用法和 VBScript 基本一致。注意在 VBScript 里字符串拼接用&,在 Python 里直接字符串加\r换行即可。
如果运行时提示找不到SecureCRT模块,重点检查两件事。第一,SecureCRT 版本是否支持 Python 脚本,新版通常在安装时有脚本组件选项。第二,全局选项里是否配置了 Python 解释器路径,不同版本处理方式有差异,以你所用版本的官方帮助为准。不想折腾环境的话,纯 VBScript 永远不会让你失望。
3. 实战开发:批量巡检与配置备份
3.1 设备清单怎么设计
批量巡检之前,先要解决“脚本要操作哪些设备”的问题。最笨但最稳妥的方式,是把 SecureCRT 里已经保存好的会话名写进脚本的一个数组里。脚本通过crt.Session.Connect "/S 会话名"去连接对应会话,登录用户、密码都由 SecureCRT 会话保存,脚本本身不碰敏感信息。
我更推荐的方式是把设备清单放到外部文件。每次要改设备范围时,编辑文本文件就行,不用动脚本代码。文件内容很简单,一行一个会话名:
R1 R2 R3 SW-1 SW-2脚本读取这个文件,逐个连接、执行命令、断开连接。这样做的另一个好处是:设备多的时候,可以只改清单文件就跑一轮,不用重新打开脚本编辑器。
关于密码,我强烈建议不要写进脚本。脚本代码经常会分享给同事看,放在 Git 仓库里,明文密码一旦提交,基本上就等于泄露了。SecureCRT 会话本身可以保存密码,让脚本去连接“已经保存凭据的会话”,既安全又省事。如果你确实需要动态指定 IP 登录,也可以用crt.Session.Connect "/SSH2 192.168.1.1 /U user"这类方式,但密码参数我一般只放在一次性临时脚本里,用完即删。
3.2 完整脚本:登录、关闭分页、抓配置
下面是一个我实际在用的 VBScript 批量备份脚本简化版。目标设备是 Cisco 路由器,提示符是#,会话名来自E:\auto\devices.txt,每台设备备份出来的配置文件写到E:\auto\backup\目录。
Sub Main() Dim fso, file, sName Set fso = CreateObject("Scripting.FileSystemObject") ' 读取设备清单 Set file = fso.OpenTextFile("E:\auto\devices.txt", 1) Do Until file.AtEndOfStream sName = Trim(file.ReadLine) If Len(sName) > 0 Then Call BackupOneDevice(sName) End If Loop file.Close crt.Dialog.MessageBox "批量备份完成,失败设备请看 fail.txt。" End Sub Sub BackupOneDevice(sName) Dim logName Dim okFlag ' 连接已保存的会话 crt.Session.Connect "/S " & sName If crt.Session.IsConnected Then ' 先按回车,出现提示符后再继续 crt.Screen.Send vbCr okFlag = crt.Screen.WaitForString("#", 15) If okFlag Then ' 关闭分页输出,防止配置在 --More-- 处卡住 crt.Screen.Send "terminal length 0" & vbCr crt.Screen.WaitForString "#", 10 ' 打开日志,保存配置输出 logName = "E:\auto\backup\" & sName & "_" & GetDateStr() & ".log" crt.Session.LogFileName = logName crt.Session.Log True ' 抓取配置 crt.Screen.Send "show running-config" & vbCr crt.Screen.WaitForString "#", 30 ' 关闭日志 crt.Session.Log False End If crt.Session.Disconnect crt.Sleep 1000 Else ' 连接失败则记录到失败文件 WriteFailInfo sName End If End Sub Function GetDateStr() Dim y, m, d y = Year(Now) m = Month(Now) d = Day(Now) If m < 10 Then m = "0" & m If d < 10 Then d = "0" & d GetDateStr = y & m & d End Function Sub WriteFailInfo(sName) Dim fso, file Set fso = CreateObject("Scripting.FileSystemObject") Set file = fso.OpenTextFile("E:\auto\fail.txt", 8, True) file.WriteLine Now & " " & sName & " 连接失败" file.Close End Sub这段脚本里有几个细节值得展开说。
第一,crt.Session.Connect "/S " & sName要求 SecureCRT 里已经存在名为sName的会话。如果你的会话放在特定文件夹里,可能需要写“文件夹/会话名”这样的路径。建议先在 SecureCRT 连接管理里把要巡检的会话都整理好,再跑脚本。
第二,terminal length 0是 Cisco 设备关闭输出分页的命令。很多设备默认输出超过一屏会显示--More--等待按键,如果不先关闭分页,show running-config输出到一半就会卡住,脚本等提示符等不到,直接超时。华为设备对应的命令是screen-length 0 temporary,执行前先想清楚目标设备是什么厂商。这一步不做,后面的日志抓取大概率翻车。
第三,日志的开关要“开在命令发送之前,关在提示符等到之后”。crt.Session.Log True开始记录,此时脚本发送show running-config,屏幕上出现的内容都会写进日志文件;等提示符再次出现,说明命令输出完毕,立刻执行crt.Session.Log False关闭。这样日志文件里恰好是完整的一条命令输出,没有多余内容。
这里我踩过一个坑:以前图省事,先发送命令再开日志,结果日志文件里什么都记录不到。SecureCRT 的日志记录的是“屏幕收到的新内容”,不是历史缓冲区,所以要先把日志开关打开,再触发输出。
3.3 结果归档与失败统计
备份脚本跑完后,E:\auto\backup\目录下会生成类似R1_20250101.log的文件。文件名带上日期,方便按天归档。如果你要做配置对比,可以把每天的备份目录单独建一个文件夹,或者文件名再带上时间戳,比如_20250101_1530。
失败信息写到fail.txt的意义,不是让你事后人工翻,而是让整个批量过程有迹可查。脚本弹窗提示“批量备份完成”,你只需要打开 fail.txt 看哪几台没连上,再单独处理。几十台设备巡检,偶尔一两台网络抖动的很正常,脚本能帮你把“连接成功但命令没执行完”和“根本没连上”这两类情况区分开,省很多排查时间。
另外一个经验是:日志文件写入的目录必须提前创建好。FileSystemObject的OpenTextFile不会帮你建目录,如果E:\auto\backup\不存在,脚本会在设置LogFileName时报错。我习惯在脚本开头加一段“如果目录不存在则创建”的逻辑,后面就不会被这种低级问题打断。
If Not fso.FolderExists("E:\auto\backup") Then fso.CreateFolder("E:\auto\backup") End If4. 进阶技巧:配置下发与关键字监控
4.1 批量改配置,安全是第一优先级
备份是只读操作,风险低。但自动化脚本不只是能看配置,还能改配置。这就必须谨慎了。我的原则是:批量下发命令之前,先在单个测试设备上完整跑通,再扩大范围。并且命令制定好后,至少要经过一次人工 review。
批量修改设备配置的典型流程是这样:进入特权模式,进入全局配置模式,逐条发送配置命令,退出后保存配置,再执行show类命令验证结果。以 Cisco 设备为例,核心片段如下:
' 进入特权模式 crt.Screen.Send "enable" & vbCr crt.Screen.WaitForString "Password", 10 crt.Screen.Send "特权密码" & vbCr crt.Screen.WaitForString "#", 10 ' 进入全局配置模式 crt.Screen.Send "configure terminal" & vbCr crt.Screen.WaitForString "(config)#", 10 ' 下发配置命令 crt.Screen.Send "snmp-server community public Read-Only" & vbCr crt.Screen.WaitForString "(config)#", 10 crt.Screen.Send "access-list 100 permit udp any any snmp" & vbCr crt.Screen.WaitForString "(config)#", 10 ' 退出配置模式 crt.Screen.Send "end" & vbCr crt.Screen.WaitForString "#", 10 ' 保存配置 crt.Screen.Send "write memory" & vbCr crt.Screen.WaitForString "#", 30说几个容易忽略的点。
提示符从#变成了(config)#,脚本里必须等待对应的提示符再继续。很多人写批量下发,习惯统一等#,但配置模式下的提示符明显不一样,WaitForString "#"也能匹配到(config)#里的#,这样万一命令实际没进入配置模式,脚本照样往下走,就会错上加错。
发送配置命令时,我不建议把整段配置一次性Send一个大字符串。一条条发、一条条等,速度慢一点,但可控。配置命令通常语法要求严格,一旦命令错误,设备会立即报错,等提示符出现后你还能从屏幕看出问题。一条条发也有利于日志文件里清晰地记录每一条命令和响应。
最后,保存配置这一步不能省。配置文件不保存,设备一重启,改动全部丢失,巡检的人还得返工。write memory在部分设备可能需要几十秒,所以这个WaitForString超时我给到 30 秒,宁可等得久一点,也不要超时后误判。
4.2 输出里的关键字监控
有时候你不想备份全部配置,只想巡检结果,比如看看哪些接口 down 了、日志里有没有 error、设备运行时长是否异常。这类需求可以做成“抓取输出,匹配关键字,输出报告”的小工具。
SecureCRT 脚本想要判断屏幕输出内容,最直接的方式是记录日志,再用脚本读取日志文件。因为 SecureCRT 的屏幕缓冲区本身就是连续的字符流,你用ReadString去读可能受光标位置、回显模式影响,很容易读得不全。把输出写入日志后,用文本文件处理,稳定可靠。
下面这段 VBScript 展示了怎么从日志文件里查找关键字:
Function FileContainsKey(logPath, keyword) Dim fso, file, content, has Set fso = CreateObject("Scripting.FileSystemObject") Set file = fso.OpenTextFile(logPath, 1) content = file.ReadAll file.Close If InStr(1, content, keyword, vbTextCompare) > 0 Then has = True Else has = False End If FileContainsKey = has End Function用法也简单:每台设备执行完关键命令后,日志文件已经生成,调用这个函数检查是否包含你想找的关键字。比如:
If FileContainsKey(logName, "line protocol is down") Then ' 把设备名和关键字写进异常报告 End If这个方案对巡检特别实用。巡检的目的不是每台设备都读一遍输出,而是快速筛掉正常的,只留异常的。脚本自动帮你完成筛选,最后报告里只有问题设备,效率翻倍。
4.3 哪些命令绝对不能写进自动化脚本
自动化能做的范围很广,但不是所有命令都适合写进脚本。我总结了一张表,代表我自己的安全底线:
| 命令类型 | 风险等级 | 是否建议自动化 | 说明 |
|---|---|---|---|
| show 类只读命令 | 低 | 建议 | 巡检、备份主力 |
| terminal length / screen-length | 低 | 建议 | 关闭分页,防卡输出 |
| copy running-config startup-config | 中 | 可以,但要等待 | 耗时较长,注意超时 |
| write memory | 中 | 可以,但要等待 | 同上 |
| configure terminal 下的常规配置 | 中 | 谨慎 | 先测试设备验证,再批量 |
| reload / reboot | 高 | 不建议 | 会导致业务中断,需有人工确认 |
| delete / erase / format | 高 | 不建议 | 不可逆,人工执行并复核 |
| 登录密码修改类命令 | 高 | 不建议 | 一旦错误可能把自己锁在外面 |
这条原则我一直在用:脚本处理的应该是“可重复、可验证、低风险”的操作。可重复意味着就算执行两遍结果一样;可验证意味着执行后能通过show或日志确认效果;低风险意味着即使当时没人盯着,出问题也在可控范围内。凡是违背这三条的,一律保留人工执行。
5. 常见问题与排查技巧
5.1 脚本跑不起来,先查这几点
脚本运行不成功,原因通常集中在这么几个地方。
一是脚本菜单是灰的。这表示 SecureCRT 当前没有活动会话,或者当前标签页没有连接。脚本是对“某个终端会话”操作的,必须先手动连接一台设备,或者至少让 SecureCRT 处于有标签页打开的状态。
二是报语法错误。最常见的原因是编辑器把双引号转成了中文引号。VBScript 只认英文双引号,代码里一旦混入中文引号,看起来一模一样,运行直接报错。这个坑我踩过不止一次,现在的习惯是所有脚本文件都用普通文本编辑器写,写完检查一遍引号,再放到 SecureCRT 里跑。
三是文件路径不对。脚本里用相对路径很危险,因为 SecureCRT 的工作目录不一定是脚本所在目录。我统一用绝对路径,最省心。尤其注意OpenTextFile的路径分隔符是反斜杠\,别在 Python 里写成/,VBScript 里写成\\虽然也能跑但没必要。
5.2 WaitForString 等不到提示符
这是 SecureCRT 自动化脚本里最经典的问题。你明明觉得设备已经返回提示符了,脚本却在那干等,最后超时。
我总结下来主要原因有三个。
第一,提示符和你WaitForString的内容不一致。不同设备提示符差异很大,可能是#、>、$、Router>、Switch#,还有的用户名就是提示符的一部分。解决方法是:先手动连一次设备,把屏幕上真实的提示符原样复制出来,再写进脚本。不要凭印象,不要猜。
第二,终端输出有颜色控制字符。SecureCRT 的 ANSI 彩色终端会在输出里插入转义序列,这些字符肉眼看不见,但WaitForString匹配的是原始字符流,可能因此匹配失败。遇到这种情况,检查设备的显示配置,尽量用黑色终端或者纯文本输出模式。真的无法避免时,等一个更稳定的子串,比如#之前一定会出现的那段固定文本。
第三,命令输出没有结束。最典型的就是分页没关闭,设备停在--More--,脚本满屏找提示符也找不到。前面已经强调了,执行可能产生大量输出的命令之前,先发送terminal length 0或screen-length 0 temporary。
还有一个我常用的排查方法:脚本超时后,不要急着改代码,先看看设备屏幕上此时是什么状态。SecureCRT 保持的是真实交互界面,脚本操作时屏幕一直可见。超时后屏幕上停在哪一屏,基本上就是脚本卡在哪一步。很多时候问题一目了然。
5.3 日志文件乱码或者为空
日志文件为空,我遇到过的原因有两个。一是LogFileName设置后,路径不存在;二是Log True打开之前命令就已经执行完并输出了,日志里自然没内容。解决方法就是先打开日志,再发送命令,这个顺序一定要保证。
乱码问题就复杂一些。SecureCRT 终端显示正常,不代表日志文件保存时编码正常。设备输出一般是 ASCII 拉丁字符,常规配置没问题;如果设备里配置描述、主机名等含中文,日志文件默认编码不是 UTF-8,就可能出现乱码。SecureCRT 的会话选项里有日志编码相关设置,不同版本位置略有差异。我通常把日志设置成 UTF-8,并且用支持 UTF-8 的编辑器打开日志文件。在脚本里做关键字匹配的时候,注意文件编码要和OpenTextFile读取方式一致,否则 InStr 匹配中文关键字会失败。
5.4 调试脚本的实用技巧
调试 SecureCRT 脚本,我用的工具比较朴素,但很有效。
第一招,弹窗输出。在关键步骤后面加crt.Dialog.MessageBox "当前步骤执行完毕,变量 xxx 的值是 ..."。脚本跑的时候,每弹一次窗,你既知道流程走到哪一步,又能看到变量值,比什么高级调试器都直观。临时调试代码用完之后删掉就行。
第二招,分批执行。长脚本第一次跑的时候,不要指望一次通过。把Sub Main里的大逻辑拆分成几个Sub,先只调用第一个Sub,确认没问题了,再把第二个加上。每一步都验证,调试成本最低。尤其在批量脚本里,先跑一台设备,再跑全部。
第三招,日志对照。SecureCRT 自己的菜单里有 Log Session 功能,脚本运行的同时记录一份完整交互日志。脚本跑完,直接打开这份日志,看设备到底收到了什么。别凭感觉猜,数据不会骗你。比如怀疑某个命令没发到设备上,日志一看便知。
5.5 最后一条:别把合规风险写进脚本
脚本自动化的边界,不只是技术问题,也是合规问题。批量操作的范围越广,影响面就越大。一条命令下发给 100 台设备之前,要想清楚它对业务的影响,以及权限边界。我自己的惯例是:只对权限范围内的设备跑脚本,所有自动化操作保留日志,变更类操作额外做审批和复核。
再说一个和工具本身相关的问题:SecureCRT 是商业软件,脚本功能是它的合法能力,但请使用你已获得授权的版本。不要为了省授权费去用破解版或者找奇怪渠道的注册码,那些东西本身就有安全风险,还可能帮你把带后门的文件装进运维环境。这个行业里因为用了来路不明的工具导致设备被控制、信息泄露的例子并不少。自动化本来是为了提高效率,别让它变成风险入口。
写在最后的一点体会
文本里最后一段,不打算做什么总结,就分享几个经年累月踩坑换来的习惯。
第一,Send和WaitForString必须成对出现。我见过太多人为了追求“快”,Send 完命令立刻执行下一条 Send,结果就是设备还在处理上一条命令,下一条命令已经压在终端里,交互顺序全乱。SecureCRT 脚本的哲学就是“发一条、等一条、确认一条”,多等一秒不可怕,命令乱发才可怕。
第二,新脚本永远先在模拟器或者一台无关紧要的测试设备上跑通,再放到生产环境。你永远不会知道生产环境的提示符和模拟器差了多远,等提示符不匹配导致几十台设备全部中断的时候,再好的脚本也救不了场。
第三,脚本里的每一条命令、每一个超时时间、每一次日志开关,都要能让另一个工程师看懂。批量脚本不是个人玩具,它会被同事 review,会被后来的人维护。注释写清楚“这条命令为什么等 30 秒”,比写一百行代码有价值。
自动化脚本不可能替代所有人工操作,但它能把高频、重复、确定性高的那 80% 从你手里拿走,让你有空去处理真正需要动脑的事。这是我到现在还在维护这套脚本的原因。希望这篇实战笔记能让你在 SecureCRT 的使用上,迈过“只会连、不会自动”的那道坎。