如何用Python脚本连接定制Shell网络设备并执行多命令序列?
问题:Fabric连接定制Shell网络设备无法执行命令?
我有一台需通过用户名密码连接的网络设备,登录后会显示多行登录横幅,随后进入厂商预设的定制Shell。我需要在单个会话中执行一系列命令(比如先切换到IP子菜单,再执行SET IP命令),但用Fabric的Connection.run()时,只会弹出交互式Shell,没法执行指令,示例代码如下:
from fabric import Connection from invoke.exceptions import UnexpectedExit from invoke.watchers import Responder def run_network_command(host, username, password, command): try: conn = Connection(host=host, user=username, connect_kwargs={ "password": password, }, ) # Create responder for the custom shell prompt prompt_pattern = r"\[net-7\.2\] \w+>" shell_responder = Responder( pattern=prompt_pattern, response=f"{command}\n" ) # Run command in the custom shell result = conn.run( command, pty=True, watchers=[shell_responder] ) return result.stdout except UnexpectedExit as e: return f"Error executing command: {str(e)}" except Exception as e: return f"Connection error: {str(e)}" finally: try: conn.close() except: pass # Example usage if __name__ == "__main__": # Connection details host = "192.168.1.1" username = "root" password = "pass" command = "show version" # Run command and print output output = run_network_command(host, username, password, command) print(output)
解决方法
1. 核心问题分析
你的代码只处理了定制Shell的提示符,但忽略了完整的登录交互链:用户名输入提示、密码输入提示、登录横幅后的首次提示符匹配。另外,conn.run(command)是尝试在远程标准Shell执行命令,而定制Shell不支持这种调用方式,必须在交互式会话中逐步响应提示符并发送命令。
2. 关键改进方案
- 覆盖全交互节点:针对登录时的用户名、密码提示,以及后续的Shell/子菜单提示符,分别创建
Responder处理。 - 改用
conn.shell():该方法专门用于交互式会话,更适配定制Shell的场景,而非conn.run()。 - 支持多命令序列:按顺序匹配每个步骤的提示符,依次发送命令,比如进入子菜单→执行配置→返回主菜单。
3. 修正后的代码
from fabric import Connection from invoke.watchers import Responder import time def run_network_commands(host, username, password, commands): try: # 定义登录阶段的响应器 user_responder = Responder( pattern=r"Username:|login:", # 匹配设备的用户名提示 response=f"{username}\n" ) pass_responder = Responder( pattern=r"Password:", # 匹配设备的密码提示 response=f"{password}\n" ) # 初始化连接 conn = Connection(host=host, user=username, connect_kwargs={"password": password}) # 启动交互式Shell会话,开启pty确保交互正常 with conn.shell(pty=True, watchers=[user_responder, pass_responder]) as shell: # 等待登录横幅加载完成,确保提示符出现 time.sleep(2) # 逐行发送命令序列 for cmd in commands: shell.send(f"{cmd}\n") # 等待命令执行完成,根据设备响应速度调整sleep时长 time.sleep(1) # 最后发送退出命令 shell.send("exit\n") # 等待会话结束,读取所有输出 time.sleep(1) output = shell.stdout.read().decode('utf-8') return output except Exception as e: return f"执行异常: {str(e)}" finally: try: conn.close() except: pass # 示例:执行多步命令序列 if __name__ == "__main__": host = "192.168.1.1" username = "root" password = "pass" # 命令序列:查看版本 → 进入IP子菜单 → 设置IP地址 → 退出子菜单 → 退出Shell commands = ["show version", "ip", "set ip 192.168.1.2", "exit", "exit"] output = run_network_commands(host, username, password, commands) print(output)
4. 额外注意事项
- 正则表达式匹配:要根据设备的实际输出调整
pattern,比如子菜单的提示符可能是IP-SUBMENU>,需要把这类情况加入匹配规则。 - 等待时长调整:
time.sleep()的时长要根据设备的响应速度调整,避免命令发送过快导致未匹配到提示符。 - 调试技巧:可以在启动
shell时开启实时输出查看,方便调整匹配规则和等待时长。
内容的提问来源于stack exchange,提问作者Amol
相关产品推荐
相关产品推荐

