You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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命令并读取对应输出(可使用或不使用第三方库)。请问问题出在哪里?


问题根源

  1. 环境不匹配:用ChannelShell连接Windows服务器时,默认启动的是Windows的cmd.exe,而非bash或PowerShell。发送的#!/bin/bash是Unix脚本头,对Windows完全无效;ls -l是Unix命令,cmd.exe无法识别。
  2. 退出条件错误:代码用line.contains("logout")作为循环终止条件,但Windows的cmd.exe退出时不会输出logout,导致循环无法正确终止。
  3. 命令发送方式错误:把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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 02:15:53