- 核心概念:SecureCRT 如何与 Python 交互?
- 环境准备:安装必要的库
- 基础脚本示例:连接设备并执行命令
- 进阶脚本示例:处理交互式命令(如
configure terminal) - 高级功能:处理多行输出、捕获日志、文件传输
- 完整实战案例:备份多台设备的配置
- 调试与排错技巧
核心概念:SecureCRT 如何与 Python 交互?
SecureCRT 提供了一个名为 crt 的 Python 模块,这个模块是 Python 脚本与 SecureCRT 应用程序之间的桥梁。

crt对象:这是整个脚本的入口点,代表 SecureCRT 应用程序本身,你可以通过它获取当前会话、屏幕对象、选项等。crt.Screen对象:这是最核心的对象,代表当前活动会话的虚拟屏幕,你可以通过它来:- 发送按键到远程设备(
.Send()) - 读取屏幕上的内容(
.GetString()) - 检测光标位置(
.X,.Y) - 等待特定文本出现(
.WaitForString())
- 发送按键到远程设备(
crt.Session对象:代表当前的连接会话,你可以通过它获取连接信息,如主机名、用户名等。
重要提示:SecureCRT 的 Python 脚本不是独立的脚本,它必须在 SecureCRT 应用程序内部运行,你可以在 SecureCRT 中通过菜单 Scripting -> Run 来执行它。
环境准备:安装必要的库
你不需要用 pip 安装任何额外的库,SecureCRT 自带并集成了所有必要的 Python 环境,你只需要确保你的系统上安装了 Python(SecureCRT 通常会自带一个轻量级的 Python 环境)。
准备工作:
- 安装 SecureCRT。
- 确保你已经配置好需要连接的设备会话(Session),并且知道设备的登录凭据(用户名/密码,或者支持密钥认证)。
基础脚本示例:连接设备并执行命令
这个脚本将演示如何连接到一台设备,发送几个基本命令,并打印输出结果。

目标:登录到一台 Cisco 路由器,执行 show version 和 show running-config | include hostname。
脚本 (basic_commands.py):
# -*- coding: utf-8 -*-
import crt
import re
import sys
def main():
"""
主函数:执行基本命令并捕获输出
"""
# 1. 获取当前活动会话的屏幕对象
screen = crt.Screen
# 2. 定义一些常量,方便修改
# 注意:如果你的密码包含特殊字符,可能需要进行转义
# 密码 "P@ssw0rd" 在脚本中应写成 "P\@ssw0rd"
USERNAME = "your_username"
PASSWORD = "your_password"
ENABLE_PASSWORD = "your_enable_password" # 如果设备进入特权模式需要密码
# 3. 发送回车键,确保会话是活跃的(如果已经登录,这可以防止脚本卡在登录提示)
screen.Send("\r")
# 4. 等待登录提示出现,超时时间为10秒
# WaitForString 是脚本稳定性的关键,它能防止脚本在设备响应慢时出错
if not screen.WaitForString("login:", 10):
crt.Dialog.MessageBox("未找到登录提示,脚本退出。", "错误")
sys.exit(1)
# 5. 输入用户名并发送回车
screen.Send(USERNAME + "\r")
# 6. 等待密码提示
if not screen.WaitForString("Password:", 5):
crt.Dialog.MessageBox("未找到密码提示,脚本退出。", "错误")
sys.exit(1)
# 7. 输入密码(密码输入时不显示在屏幕上)
screen.Send(PASSWORD + "\r")
# 8. 等待设备提示符(Cisco 的 ">" 或 "#")
# 我们等待一个通用的模式,表示已经成功登录
if not screen.WaitForString(r"[>#]", 10):
crt.Dialog.MessageBox("登录失败,未找到设备提示符。", "错误")
sys.exit(1)
# 9. 进入特权模式(如果需要)
screen.Send("enable\r")
if not screen.WaitForString("Password:", 5):
# 如果没有 enable password,可能会直接进入
if not screen.WaitForString(r"#", 5):
crt.Dialog.MessageBox("进入特权模式失败。", "错误")
sys.exit(1)
else:
screen.Send(ENABLE_PASSWORD + "\r")
if not screen.WaitForString(r"#", 5):
crt.Dialog.MessageBox("输入 enable 密码后,未找到特权提示符。", "错误")
sys.exit(1)
# 10. 执行命令并捕获输出
print("--- 执行 'show version' ---")
screen.Send("show version\r")
# 等待命令执行完成,等待新的提示符出现
# WaitForString 的第二个参数是超时时间(秒)
screen.WaitForString(r"#", 10)
# 获取从命令执行开始到结束的所有文本
# screen.Get() 获取从屏幕顶部到当前光标位置的文本
# 更精确的方法是记录命令开始前的位置,然后获取到当前位置的文本
output = screen.Get(screen.CurrentRow - 1, 0, screen.CurrentRow, 80)
print(output)
print("\n--- 执行 'show running-config | include hostname' ---")
screen.Send("show running-config | include hostname\r")
screen.WaitForString(r"#", 10)
output = screen.Get(screen.CurrentRow - 1, 0, screen.CurrentRow, 80)
print(output)
print("\n脚本执行完成。")
if __name__ == "__main__":
main()
如何使用:
- 将上述代码保存为
basic_commands.py。 - 在 SecureCRT 中,打开一个已经配置好的设备会话。
- 在 SecureCRT 菜单栏中,选择
Scripting -> Run。 - 浏览并选择你刚刚保存的
basic_commands.py文件,然后点击 "Run"。 - 查看脚本输出(SecureCRT 的 "Scripting" 窗口)和终端显示。
进阶脚本示例:处理交互式命令
很多配置命令是交互式的,需要你输入 y/n 或者提供更多参数。WaitForString 在这里大显身手。

目标:关闭一个接口。
脚本 (interactive_command.py):
# -*- coding: utf-8 -*-
import crt
import sys
def main():
screen = crt.Screen
USERNAME = "your_username"
PASSWORD = "your_password"
ENABLE_PASSWORD = "your_enable_password"
# --- 登录部分 (与上面示例相同,此处省略) ---
screen.Send("\r")
screen.WaitForString("login:", 10)
screen.Send(USERNAME + "\r")
screen.WaitForString("Password:", 5)
screen.Send(PASSWORD + "\r")
screen.WaitForString(r"[>#]", 10)
screen.Send("enable\r")
screen.WaitForString("Password:", 5)
screen.Send(ENABLE_PASSWORD + "\r")
screen.WaitForString(r"#", 5)
# --- 登录结束 ---
# 1. 进入全局配置模式
print("进入全局配置模式...")
screen.Send("configure terminal\r")
# 等待 "(config)#" 提示符
screen.WaitForString(r"(config)#", 5)
# 2. 进入接口配置模式
print("进入接口 GigabitEthernet0/1 配置模式...")
screen.Send("interface GigabitEthernet0/1\r")
# 等待 "(config-if)#" 提示符
screen.WaitForString(r"(config-if)#", 5)
# 3. 执行关闭接口的命令
print("执行 'shutdown' 命令...")
screen.Send("shutdown\r")
# 4. 处理交互式提示
# 设备可能会问 "Command rejected: Interface GigabitEthernet0/1 is not active."
# 或者 "Interface GigabitEthernet0/1 is down"
# 我们等待 "down" 这个词,并认为这是一个成功的关闭
if screen.WaitForString("down", 5):
print("接口已成功关闭。")
else:
# 如果等待超时,可能是其他情况
print("接口状态可能未改变或出现错误。")
# 5. 退出配置模式
screen.Send("end\r")
screen.WaitForString(r"#", 5)
print("脚本执行完成。")
if __name__ == "__main__":
main()
高级功能
A. 处理多行输出和分页符
很多设备的命令输出(如 show run)会分页显示,需要你按空格键或回车键继续。
技巧:在发送命令后,循环检测并发送空格键,直到最后一页。
def send_command_and_wait(screen, command, prompt=r"[>#]", timeout=10):
"""发送命令并处理分页,直到返回到提示符"""
screen.Send(command + "\r")
# 循环检测 "---- More ----" 或类似的分页提示
while screen.WaitForString("---- More ----", 2):
screen.Send(" ") # 发送空格键继续
# 等待一小段时间,防止设备响应不过来
crt.Sleep(500) # 暂停500毫秒
# 等待最终提示符出现
screen.WaitForString(prompt, timeout)
# 获取所有输出
# 这里使用一个更健壮的方法:获取从命令行开始到当前行的所有内容
# 你需要先记录命令发送后的行号,或者简单获取屏幕上的大部分内容
#
# output = screen.Get(0, 0, screen.CurrentRow, 80)
# return output
return "命令执行完毕,输出已捕获。"
B. 捕获日志
你可以将脚本的输出重定向到一个文件中,或者直接将屏幕内容保存为日志文件。
def log_to_file(screen, filename="output.log"):
"""将当前屏幕内容保存到文件"""
with open(filename, "w", encoding="utf-8") as f:
# 获取整个屏幕的内容
f.write(screen.Get(0, 0, screen.Rows, screen.Columns))
crt.Dialog.MessageBox(f"日志已保存到 {filename}", "信息")
C. 文件传输 (SCP/FTP)
SecureCRT 的 Python 脚本本身不直接支持 SCP 或 FTP,但你可以通过 os.system 模块调用系统的命令行工具(如 scp, pscp.exe)来实现。
示例 (使用 pscp.exe,PuTTY 的 SCP 客户端):
假设 pscp.exe 在你的系统 PATH 中,或者你指定其完整路径。
import os
def backup_config_via_scr_ip(hostname, remote_file, local_file, username, password):
"""通过 pscp.exe 下载文件"""
# 注意:pscp 的密码参数是 -pw,并且文件路径格式是 [user@]host:file
command = f'pscp.exe -pw {password} {username}@{hostname}:{remote_file} {local_file}'
# 执行系统命令
# os.system(command) # 简单执行,不捕获输出
# 更好的方式是使用 subprocess 模块,可以获取返回码和输出
import subprocess
try:
result = subprocess.run(command, check=True, capture_output=True, text=True, shell=True)
print(f"文件下载成功: {local_file}")
print(result.stdout)
except subprocess.CalledProcessError as e:
print(f"文件下载失败: {e}")
print(e.stderr)
# 在你的主脚本中调用这个函数
# backup_config_via_scr_ip("192.168.1.1", "running-config", "C:\\backups\\router1.cfg", "admin", "P\@ssw0rd")
完整实战案例:备份多台设备的配置
这个脚本将读取一个设备列表文件,依次登录到每台设备,执行备份命令,并将配置保存到本地。
设备列表文件 (devices.txt):
# 格式: 设备名称,IP地址,用户名,密码,Enable密码,备份文件名
Router1,192.168.1.1,admin,P@ssw0rd,enable_pass,router1_running.cfg
Switch1,192.168.1.2,admin,P@ssw0rd,enable_pass,switch1_running.cfg
脚本 (batch_backup.py):
# -*- coding: utf-8 -*-
import crt
import sys
import os
import time
# --- 配置 ---
DEVICE_LIST_FILE = "devices.txt"
BACKUP_DIR = "C:\\SecureCRT_Backups"
LOG_FILE = os.path.join(BACKUP_DIR, "backup_log.txt")
def log_message(message):
"""打印消息并记录到日志文件"""
print(message)
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(f"[{timestamp}] {message}\n")
def backup_device_config(screen, hostname, ip, username, password, enable_password, backup_filename):
"""备份单台设备的配置"""
log_message(f"--- 开始备份设备: {hostname} ({ip}) ---")
try:
# 登录逻辑 (同前)
screen.Send("\r")
screen.WaitForString("login:", 10)
screen.Send(username + "\r")
screen.WaitForString("Password:", 5)
screen.Send(password + "\r")
screen.WaitForString(r"[>#]", 10)
screen.Send("enable\r")
screen.WaitForString("Password:", 5)
screen.Send(enable_password + "\r")
screen.WaitForString(r"#", 5)
# 发送备份命令
# 这里使用 terminal length 0 禁用分页
log_message("禁用分页...")
screen.Send("terminal length 0\r")
screen.WaitForString(r"#", 5)
log_message("执行 'show running-config'...")
screen.Send("show running-config\r")
# 等待命令执行完成
screen.WaitForString(r"#", 30)
# 获取输出
output = screen.Get(screen.CurrentRow - 1, 0, screen.CurrentRow, 80)
# 保存到文件
full_backup_path = os.path.join(BACKUP_DIR, backup_filename)
with open(full_backup_path, "w", encoding="utf-8") as f:
f.write(output)
log_message(f"成功备份 {hostname} 的配置到 {full_backup_path}")
except Exception as e:
log_message(f"备份 {hostname} 时发生错误: {e}")
finally:
# 无论成功与否,都尝试退出配置模式并断开连接
log_message("尝试退出...")
screen.Send("end\r")
screen.WaitForString(r"#", 5)
screen.Send("logout\r")
crt.Sleep(2000) # 等待几秒,让连接完全关闭
log_message(f"--- 完成 {hostname} 的备份 ---")
def main():
# 确保备份目录存在
if not os.path.exists(BACKUP_DIR):
os.makedirs(BACKUP_DIR)
log_message(f"创建备份目录: {BACKUP_DIR}")
# 清空或创建日志文件
with open(LOG_FILE, "w", encoding="utf-8") as f:
f.write(f"备份任务开始于 {time.ctime()}\n")
# 读取设备列表
try:
with open(DEVICE_LIST_FILE, "r", encoding="utf-8") as f:
devices = f.readlines()
except FileNotFoundError:
crt.Dialog.MessageBox(f"找不到设备列表文件: {DEVICE_LIST_FILE}", "错误")
sys.exit(1)
screen = crt.Screen
for line in devices:
line = line.strip()
if not line or line.startswith("#"):
continue # 跳过空行和注释
parts = [p.strip() for p in line.split(',')]
if len(parts) != 6:
log_message(f"设备列表文件格式错误,跳过行: {line}")
continue
hostname, ip, username, password, enable_password, backup_filename = parts
# 在新标签页中打开会话
# crt.Session.Connect 在脚本中可能不总是按预期工作,
# 更可靠的方式是让用户预先打开一个会话,或者使用命令行启动 SecureCRT 并连接。
# 这里我们假设脚本是在一个已打开的会话中运行的,并且会话会自动切换到下一个设备。
# 一个更健壮的方案是使用 `crt.Command` 来执行 `/SCRIPT OPEN ...` 命令,但这比较复杂。
# 为了简化,我们假设用户会手动切换到下一个设备的会话,或者脚本在一个循环中处理同一个会话(不推荐)。
# **重要提示**: 此脚本设计为在一个已打开的会话中运行。
# 要实现真正的自动化,你需要一个能动态创建和管理会话的框架,这通常需要更复杂的脚本或使用 SecureCRT 的命令行接口。
# 对于真正的批量操作,建议使用 Ansible, Nornir 等专业工具。
# 我们只是简单地执行备份逻辑
# 你需要手动在 SecureCRT 中切换到下一个设备的会话,然后重新运行脚本。
# 这不是一个完美的解决方案,但展示了核心逻辑。
backup_device_config(screen, hostname, ip, username, password, enable_password, backup_filename)
# 提示用户手动切换
if not crt.Dialog.MessageBox(f"请手动切换到下一个设备的会话,然后点击'确定'继续。", "需要手动切换", 1): # 1 是 OK/Cancel
log_message("用户取消了操作。")
break
crt.Sleep(3000) # 给用户3秒时间切换
log_message("所有设备备份任务完成。")
if __name__ == "__main__":
main()
调试与排错技巧
-
使用
crt.Dialog.MessageBox:在脚本的关键节点弹出消息框,可以确认脚本是否执行到某一步,以及变量的值是什么。crt.Dialog.MessageBox(f"当前提示符是: {screen.GetString(screen.CurrentRow, 0, 1)}") -
使用
print和日志:将变量、状态信息打印到 Python 控制台(SecureCRT 的 Scripting 窗口),这是最常用的调试方法。 -
增加
crt.Sleep():如果脚本执行太快,设备来不及响应,会导致WaitForString失败,在关键步骤之间加入短暂的延时,crt.Sleep(1000)(暂停1秒),可以大大提高脚本的稳定性。 -
精确的
WaitForString:WaitForString的模式(pattern)越精确越好,等待 提示符,用r"#"比只用 更好,可以防止匹配到行号或其他内容,使用正则表达式可以更灵活地匹配多种可能。 -
从简单开始:先写一个能成功登录并执行一条命令的脚本,然后再逐步添加功能,比如进入配置模式、处理交互、循环等,每添加一步都进行测试。
希望这份详细的指南能帮助你掌握使用 Python 脚本自动化 SecureCRT 的技能!
