基于JSch API逐行在Cisco路由器执行命令的问题排查
问题分析与修复方案
你的代码存在几个关键问题,导致无法正确获取命令执行结果:
- 命令未添加触发符:Cisco路由器需要命令后跟随回车(
\r\n)才会执行,原代码直接发送命令字符串,设备不会处理。 - 输出流未重置:
ByteArrayOutputStream在循环中没有清空,每次打印的是所有历史输出的累加结果,而非当前命令的输出。 - InputStream使用错误:每次循环替换
channel.setInputStream(),会导致流被覆盖,无法保证命令完整发送;且应该使用持续的输入流,而非每次创建新流。 - 依赖
setOutputStream不可靠:JSch的setOutputStream是将通道输出重定向,但更可靠的方式是主动读取通道的输入流来获取设备输出。
修正后的代码
/** * 逐行向路由器下发配置命令并获取执行结果 * @param configLines * @throws DeviceExcpetion */ public void deliverConfig(List<String> configLines) throws DeviceExcpetion { Channel channel = null; JSch jSch = new JSch(); Session session = null; InputStream channelInputStream = null; OutputStream channelOutputStream = null; BufferedReader reader = null; try { session = jSch.getSession(userid, ipAddress, port); session.setPassword(password); session.setConfig("StrictHostKeyChecking", "no"); session.connect(); // 打开shell通道 channel = session.openChannel("shell"); channelInputStream = channel.getInputStream(); channelOutputStream = channel.getOutputStream(); reader = new BufferedReader(new InputStreamReader(channelInputStream, StandardCharsets.UTF_8)); channel.connect(); // 先读取初始提示符,确保设备就绪 waitForPrompt(reader); for (String line : configLines) { // 发送命令,添加回车符触发执行 String command = line + "\r\n"; channelOutputStream.write(command.getBytes(StandardCharsets.UTF_8)); channelOutputStream.flush(); // 等待命令执行完成并读取输出 String response = readUntilPrompt(reader); System.out.println("命令[" + line + "]执行结果:\n" + response); } } catch (JSchException | IOException | InterruptedException e) { e.printStackTrace(); throw new DeviceExcpetion(e.getMessage()); } finally { // 关闭资源 try { if (reader != null) reader.close(); if (channelInputStream != null) channelInputStream.close(); if (channelOutputStream != null) channelOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } if (channel != null) channel.disconnect(); if (session != null) session.disconnect(); } } /** * 等待设备提示符出现(适配Cisco常见提示符如Router>、Router#) */ private void waitForPrompt(BufferedReader reader) throws IOException, InterruptedException { StringBuilder sb = new StringBuilder(); while (true) { if (reader.ready()) { char c = (char) reader.read(); sb.append(c); String content = sb.toString(); // 判断是否出现提示符,可根据实际设备调整正则 if (content.matches(".*[>#]$")) { break; } } else { Thread.sleep(200); } } } /** * 读取输出直到提示符出现,返回命令执行结果(不含提示符) */ private String readUntilPrompt(BufferedReader reader) throws IOException, InterruptedException { StringBuilder sb = new StringBuilder(); while (true) { if (reader.ready()) { char c = (char) reader.read(); sb.append(c); String content = sb.toString(); // 判断是否出现提示符 if (content.matches(".*[>#]$")) { // 去掉最后的提示符部分 int promptIndex = content.lastIndexOf(content.charAt(content.length()-1)); return content.substring(0, promptIndex).trim(); } } else { Thread.sleep(200); } } }
关键修改说明
- 添加命令触发符:所有命令后添加
\r\n,确保Cisco设备识别并执行命令。 - 主动读取通道输入流:通过
channel.getInputStream()获取设备输出,使用BufferedReader逐字符读取,可靠性更高。 - 等待提示符机制:通过判断设备提示符(如
>或#)确定命令执行完成,替代固定Thread.sleep(),适配不同设备的响应速度。 - 资源正确关闭:在finally块中关闭所有IO流,避免资源泄漏。
- 单次输出隔离:每次读取到提示符后返回当前命令的输出,不会累计历史内容。
内容的提问来源于stack exchange,提问作者hell_storm2004
相关产品推荐
相关产品推荐

