使用JSch从Unix客户端SSH到Windows服务器无法读取命令输出
问题:通过JSch连接Windows服务器执行命令无预期输出
我尝试使用JSch的ChannelShell登录远程Windows服务器,执行Shell命令并读取输出,但readChannelOutput方法并未打印对应命令的输出(比如执行ls -l本该列出目标机器的文件和子目录)。
我的代码实现
private static Session getSession() { if (session == null || !session.isConnected()) { session = connect(hostname, username, password); } return session; } private static Channel getChannel() { if (channel == null || !channel.isConnected()) { try { channel = (ChannelShell) getSession().openChannel("shell"); channel.connect(); } catch (Exception e) { System.out.println("Error while opening channel: " + e); } } return channel; } private static Session connect(String hostname, String username, String password) { JSch jSch = new JSch(); try { session = jSch.getSession(username, hostname, 22); Properties config = new Properties(); config.put("StrictHostKeyChecking", "no"); session.setConfig(config); session.setPassword(password); System.out.println("Connecting SSH to " + hostname + " - Please wait for few seconds... "); session.connect(); System.out.println("Connected!"); } catch (Exception e) { System.out.println("An error occurred while connecting to " + hostname + ": " + e); } return session; } static void executeCommands(List<String> commands) { try { Channel channel = getChannel(); System.out.println("Sending commands..."); sendCommands(channel, commands); readChannelOutput(channel); System.out.println("Finished sending commands!"); } catch (Exception e) { System.out.println("An error ocurred during executeCommands: " + e); } } private static void sendCommands(Channel channel, List<String> commands) { try { PrintStream out = new PrintStream(channel.getOutputStream()); out.println("#!/bin/bash"); for (String command : commands) { out.println(command); } out.println("exit"); out.flush(); } catch (Exception e) { System.out.println("Error while sending commands: " + e); } } private static void readChannelOutput(Channel channel) { byte[] buffer = new byte[1024]; try { InputStream in = channel.getInputStream(); String line = ""; while (true) { while (in.available() > 0) { int i = in.read(buffer, 0, 1024); if (i < 0) { break; } line = new String(buffer, 0, i); System.out.println(line); } if (line.contains("logout")) { break; } if (channel.isClosed()) { break; } try { Thread.sleep(1000); } catch (Exception ee) { } } } catch (Exception e) { System.out.println("Error while reading channel output: " + e); } }
实际输出(执行ls -l时)
Connecting SSH to 17x.xx.xx.xx - Please wait for few seconds... Connected! Sending commands... ]0;c:\windows\system32\cmd.exeMicrosoft Windows [Version 10.0.17763.3770] (c) 2018 Microsoft Corporation. All rights reserved. abc\administrator@xyz28 C:\Users\Administrator>#!/bin/bashls -lexit ]0;Administrator: c:\windows\system32\cmd.exe
核心需求
从Unix客户端通过SSH连接到Windows服务器,执行PowerShell命令并读取对应输出(可使用或不使用第三方库)。请问问题出在哪里?
问题根源
- 环境不匹配:用
ChannelShell连接Windows服务器时,默认启动的是Windows的cmd.exe,而非bash或PowerShell。发送的#!/bin/bash是Unix脚本头,对Windows完全无效;ls -l是Unix命令,cmd.exe无法识别。 - 退出条件错误:代码用
line.contains("logout")作为循环终止条件,但Windows的cmd.exe退出时不会输出logout,导致循环无法正确终止。 - 命令发送方式错误:把bash脚本头、命令、exit直接连续发送,
cmd.exe会将其当作一整行无效命令执行,自然得不到预期输出。
解决方案
方案一:使用ChannelExec直接执行PowerShell命令(推荐)
ChannelExec是JSch专门用于执行单个/批量命令的通道,无需处理交互Shell的环境问题,更适合你的需求:
static void executeCommands(List<String> commands) { Session session = getSession(); if (session == null || !session.isConnected()) { System.out.println("会话未连接"); return; } try { for (String command : commands) { ChannelExec channel = (ChannelExec) session.openChannel("exec"); // 指定PowerShell执行命令,注意转义引号 channel.setCommand("powershell.exe -Command \"" + command + "\""); InputStream in = channel.getInputStream(); // 捕获错误输出 InputStream err = channel.getErrStream(); channel.connect(); // 读取标准输出 byte[] buffer = new byte[1024]; while (in.available() > 0) { int i = in.read(buffer, 0, 1024); if (i < 0) break; System.out.print(new String(buffer, 0, i)); } // 读取错误输出 while (err.available() > 0) { int i = err.read(buffer, 0, 1024); if (i < 0) break; System.err.print(new String(buffer, 0, i)); } // 等待命令执行完成 while (!channel.isClosed()) { Thread.sleep(500); } System.out.println("\n命令执行完成,退出状态:" + channel.getExitStatus()); channel.disconnect(); } } catch (Exception e) { System.out.println("执行命令出错: " + e); } }
方案二:使用ChannelShell切换到PowerShell执行命令
如果必须使用ChannelShell,需要先启动PowerShell再发送命令:
修改sendCommands方法
private static void sendCommands(Channel channel, List<String> commands) { try { PrintStream out = new PrintStream(channel.getOutputStream()); // 先启动PowerShell out.println("powershell.exe"); // 发送PowerShell命令(支持Get-ChildItem、ls等PowerShell命令) for (String command : commands) { out.println(command); } // 先退出PowerShell,再退出cmd out.println("exit"); out.println("exit"); out.flush(); } catch (Exception e) { System.out.println("发送命令出错: " + e); } }
修改readChannelOutput的退出判断
private static void readChannelOutput(Channel channel) { byte[] buffer = new byte[1024]; try { InputStream in = channel.getInputStream(); String line = ""; while (true) { while (in.available() > 0) { int i = in.read(buffer, 0, 1024); if (i < 0) break; line = new String(buffer, 0, i); System.out.print(line); // 用print避免换行重复 } // 修改退出条件:通道关闭或检测到PowerShell退出标识 if (channel.isClosed() || line.contains("PS C:\\Users\\Administrator> exit")) { break; } Thread.sleep(500); } } catch (Exception e) { System.out.println("读取输出出错: " + e); } }
注意事项
- Windows的
cmd.exe不支持ls -l,PowerShell中可以用Get-ChildItem或其别名ls(PowerShell内置的别名,不是Unix的ls)。 - 执行PowerShell命令时,若命令包含特殊字符,需要正确转义引号。
内容的提问来源于stack exchange,提问作者vishr
相关产品推荐
相关产品推荐

