1.更新说明接上一期在v1.0.2基础上更新功能1.连接失败时自动清除旧指纹并重连解决一直连接失败的情况2.优化逻辑直接补齐ip即可连接。只有在需要更改固定网段和用户名时选择s3.重构界面更简洁高效让新手也能快速上手2.使用步骤2.1.补齐最后一段ip输入远程主机密码连接成功2.2固定远程设备名和ip在初始界面输入s选择1修改用户名选择2修改固定网段根据你的实际网络地址输入修改完成后按回车返回连接界面2.3连接中断按1可以快速重连刚刚连接的机器输入密码按2可以返回输入IP界面连接其他机器按3可以打开普通终端按q关闭终端退出3.源代码import json import subprocess import sys import time from pathlib import Path from typing import Any, Dict, Optional # --------------------------------------------------------------------------- # 终端样式ANSI # --------------------------------------------------------------------------- class _T: RST \033[0m BOLD \033[1m CYAN \033[36m GREEN \033[32m YELLOW \033[33m RED \033[31m BLUE \033[34m WHITE \033[97m def _enable_ansi_if_windows() - None: if sys.platform ! win32: return try: import ctypes handle ctypes.windll.kernel32.GetStdHandle(-11) mode ctypes.c_uint32() if ctypes.windll.kernel32.GetConsoleMode(handle, ctypes.byref(mode)) 0: return new_mode mode.value | 0x0004 ctypes.windll.kernel32.SetConsoleMode(handle, new_mode) except Exception: pass def _clear_screen() - None: print(\033[3J\033[2J\033[H, end, flushTrue) def _hr(char: str ─, width: int 65) - None: print(f{_T.WHITE}{char * width}{_T.RST}) def _blank(n: int 1) - None: for _ in range(n): print() def _title(text: str) - None: _blank(1) print(f{_T.BOLD}{_T.CYAN} {text} {_T.RST}) _blank(1) def _kv(label: str, value: str, indent: str ) - None: print(f{indent}{_T.WHITE}{label}{_T.RST} {_T.GREEN}{value}{_T.RST}) def _menu_line(key: str, desc: str, indent: str ) - None: print(f{indent}{_T.YELLOW}{key}{_T.RST} → {_T.WHITE}{desc}{_T.RST}) def _hint(text: str, indent: str ) - None: print(f{indent}{_T.WHITE}{text}{_T.RST}) def _ok(text: str) - None: print(f {_T.GREEN}✅ {text}{_T.RST}) def _warn(text: str) - None: print(f {_T.YELLOW}⚠️ {text}{_T.RST}) def _err(text: str) - None: print(f {_T.RED}❌ {text}{_T.RST}) def _prompt(text: str) - str: print(f{_T.BOLD}{_T.BLUE}{text}{_T.RST}, end, flushTrue) return input().strip() # --------------------------------------------------------------------------- # 配置EXE 兼容 # --------------------------------------------------------------------------- DEFAULT_CONFIG { remote_user: lab, ip_prefix: 192.168.77., } def get_config_path() - Path: if getattr(sys, frozen, False): base Path(sys.executable).parent else: base Path(__file__).parent return base / ssh_config.json def load_config() - Dict[str, str]: cfg DEFAULT_CONFIG.copy() path get_config_path() if path.exists(): try: data json.loads(path.read_text(encodingutf-8)) for k in cfg: if isinstance(data.get(k), str) and data[k].strip(): cfg[k] data[k].strip() except Exception: pass return cfg def save_config(cfg: dict) - None: path get_config_path() payload {k: str(cfg.get(k, DEFAULT_CONFIG[k])).strip() for k in DEFAULT_CONFIG} path.write_text(json.dumps(payload, ensure_asciiFalse, indent2), encodingutf-8) # --------------------------------------------------------------------------- # 主逻辑 # --------------------------------------------------------------------------- class RobotSSHConnector: def __init__(self): self.config load_config() self.last_host None self.last_user None def _normalize_prefix(self): prefix self.config[ip_prefix] if prefix and not prefix.endswith(.): prefix prefix . self.config[ip_prefix] prefix return prefix def settings_menu(self) - None: 设置菜单按需进入 while True: _clear_screen() _title(参数设置用户名 / IP网段) self._normalize_prefix() _kv(当前远程用户名, self.config[remote_user]) _kv(当前IP网段前缀, self.config[ip_prefix]) _blank() _hint(示例连接ssh {user}{prefix}60.format( userself.config[remote_user], prefixself.config[ip_prefix] )) _blank() _hr() _menu_line(1, 修改远程用户名) _menu_line(2, 修改IP网段前缀) _menu_line(回车, 保存并返回连接界面) _hr() _blank() choice _prompt(请选择操作) if choice : save_config(self.config) _ok(配置已保存返回连接界面) time.sleep(0.6) break if choice 1: new_user _prompt(输入新远程用户名) if new_user: self.config[remote_user] new_user save_config(self.config) _ok(用户名已更新) else: _warn(输入为空不做修改) time.sleep(0.6) elif choice 2: new_prefix _prompt(输入新网段前缀例192.168.77.) if new_prefix: if not new_prefix.endswith(.): new_prefix . self.config[ip_prefix] new_prefix save_config(self.config) _ok(网段前缀已更新) else: _warn(输入为空不做修改) time.sleep(0.6) else: _warn(无效选项) time.sleep(0.5) def _clear_host_key(self, ip_addr: str): 执行 ssh-keygen -R 清除旧主机指纹 _warn(f正在清除 {ip_addr} 的旧主机密钥记录...) try: result subprocess.run( [ssh-keygen, -R, ip_addr], capture_outputTrue, textTrue ) if result.returncode 0: _ok(f{ip_addr} 旧指纹清除成功即将重新连接) else: _warn(f清除命令执行完成{result.stderr.strip() or result.stdout.strip()}) except FileNotFoundError: _err(找不到 ssh-keygen请先安装 OpenSSH 客户端) except Exception as e: _err(f清除指纹异常{e}) time.sleep(1.0) def connect(self, user: str, host: str, auto_retry_when_key_error: bool True): self.last_user user self.last_host host target f{user}{host} cmd [ssh, -o, StrictHostKeyCheckingaccept-new, target] print(_T.RST, end, flushTrue) _clear_screen() _hint(f正在连接 → {target}) _blank() try: proc subprocess.Popen(cmd, stderrsubprocess.PIPE, stdoutsys.stdout, textTrue) _, stderr_data proc.communicate() code proc.returncode # 识别主机密钥冲突报错 key_error_flag Host key verification failed. in stderr_data if key_error_flag and auto_retry_when_key_error: _blank(2) _warn(检测到远程主机密钥变更警告自动清理旧指纹并重连) self._clear_host_key(host) return self.connect(user, host, auto_retry_when_key_errorFalse) _blank(2) _hint(fSSH会话结束退出码{code}) except FileNotFoundError: _err(未找到ssh命令请安装 OpenSSH 客户端) except Exception as e: _err(f连接发生异常{e}) def _new_cmd_tab_in_current_terminal(self): 新开终端标签/窗口 if sys.platform ! win32: subprocess.Popen(bash, shellTrue) return try: subprocess.Popen( [wt.exe, new-tab, -p, 命令提示符], creationflagssubprocess.CREATE_NO_WINDOW ) _ok(已在当前窗口新建CMD标签页) return except Exception: pass try: subprocess.Popen(cmd.exe, creationflagssubprocess.CREATE_NEW_CONSOLE) _ok(已打开独立CMD窗口未检测到Windows Terminal) except Exception as e: _err(f打开终端失败{e}) def show_reconnect_menu(self): 连接断开后的操作菜单 while True: _blank(1) _hr() _menu_line(1, 快速重连上一次主机) _menu_line(2, 返回输入IP界面) _menu_line(3, 打开新CMD终端) _menu_line(q, 退出程序) _hr() _blank() choice _prompt(请选择).lower() if choice 1: if self.last_host and self.last_user: _ok(f准备重连{self.last_user}{self.last_host}) self.connect(self.last_user, self.last_host) else: _warn(暂无连接记录无法快速重连) time.sleep(0.8) elif choice 2: _ok(返回IP输入界面) time.sleep(0.6) break elif choice 3: self._new_cmd_tab_in_current_terminal() time.sleep(1) elif choice q: _ok(程序退出) return else: _warn(无效输入请重新选择) time.sleep(0.5) def main_input_loop(self): 程序主循环默认直接进入IP输入新手最常用流程 while True: _clear_screen() _title(机器人SSH快速连接工具) self._normalize_prefix() user self.config[remote_user] prefix self.config[ip_prefix] _kv(登录账号, user) _kv(网段前缀, prefix) _blank() _hint(使用方法) _hint( ① 只输入数字例如 60→ 自动补全为 prefix 60) _hint( ② 输入完整IP例如 192.168.1.100直接使用) _hint( ③ 输入 s → 进入设置修改账号/网段) _blank() ip_input _prompt(请输入主机编号/IP) if not ip_input: continue if ip_input.lower() s: self.settings_menu() continue # 组装最终IP if . in ip_input: host ip_input else: host f{prefix}{ip_input} self.connect(user, host) self.show_reconnect_menu() def run() - None: _enable_ansi_if_windows() try: app RobotSSHConnector() app.main_input_loop() except KeyboardInterrupt: _warn(用户主动终止程序) time.sleep(0.6) except Exception as e: _err(f程序发生错误{e}) input(\n按回车键关闭窗口...) if __name__ __main__: run()4.下载链接https://wwbav.lanzouu.com/i7VlN41zmc1ghttps://wwbav.lanzouu.com/i7VlN41zmc1g密码9rw9