You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用JSch执行SSH命令时程序化处理两次交互式输入

解决JSch SSH连接中两次交互式输入的问题

嘿,我懂你现在的头疼事儿——用JSch连SSH执行命令时,既要输密码又要输额外文本(比如Automation),管道传密码还行,第二个输入死活传不进去对吧?别着急,这是因为你可能用了ChannelExec来执行命令,它更适合单命令非交互式场景,而这种需要多次交互的情况,咱们得换个思路用ChannelShell来模拟完整的shell会话!

核心解决方案:改用ChannelShell

ChannelShell会创建一个持续的SSH shell通道,就像你手动打开终端连接服务器一样,能完美支持多次交互式输入输出。下面是完整的代码示例,我会一步步给你解释:

import com.jcraft.jsch.*;
import java.io.*;

public class SSHInteractiveDemo {
    public static void main(String[] args) {
        String username = "你的用户名";
        String host = "服务器地址";
        int port = 22;
        String password = "你的密码";
        String secondInput = "Automation";
        String targetCommand = "./触发交互的脚本.sh"; // 替换成你实际要执行的命令/脚本

        try {
            JSch jsch = new JSch();
            Session session = jsch.getSession(username, host, port);
            session.setConfig("StrictHostKeyChecking", "no");
            
            // 注意:如果登录时用密钥验证不用密码,就删掉这行;如果登录需要密码,保留
            session.setPassword(password);
            session.connect();

            // 打开Shell通道
            ChannelShell channel = (ChannelShell) session.openChannel("shell");
            InputStream in = channel.getInputStream();
            OutputStream out = channel.getOutputStream();
            channel.connect();

            // 1. 发送要执行的命令,触发交互式输入流程
            out.write((targetCommand + "\n").getBytes());
            out.flush();

            // 2. 等待密码提示(实际场景建议读取输出流判断提示,这里用sleep做简化示例)
            Thread.sleep(1000);
            out.write((password + "\n").getBytes());
            out.flush();

            // 3. 等待第二个输入提示,发送指定文本
            Thread.sleep(1000);
            out.write((secondInput + "\n").getBytes());
            out.flush();

            // 读取命令执行的结果
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                System.out.print(new String(buffer, 0, bytesRead));
            }

            // 清理资源
            channel.disconnect();
            session.disconnect();
        } catch (JSchException | IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

关键细节说明

  1. 为什么ChannelExec不行?
    ChannelExec是一次性执行单个命令,它会把命令交给远程shell执行,但管道传递的输入只能被命令的第一个交互式环节捕获,第二个输入环节无法正确接收——因为管道的输入是一次性传递的,而命令的两次输入是分阶段要求的,节奏对不上。

  2. 优化输入时机(替代Thread.sleep)
    示例里用Thread.sleep是为了简化代码,实际生产环境中最好通过读取输入流,判断远程输出中出现了对应的提示(比如"Password:"、"请输入验证文本:")后再发送输入,这样更可靠。比如可以写个工具方法:

    private static void waitForPrompt(InputStream in, String prompt) throws IOException {
        StringBuilder output = new StringBuilder();
        int c;
        while ((c = in.read()) != -1) {
            output.append((char) c);
            if (output.toString().contains(prompt)) {
                break;
            }
        }
        // 打印当前输出方便调试
        System.out.print(output);
    }
    

    然后把Thread.sleep替换成waitForPrompt(in, "Password:")waitForPrompt(in, "请输入指定文本:")就行。

  3. 换行符的重要性
    每一次输入后面都要加\n,模拟终端的回车操作,这样远程shell才会识别你完成了当前输入,进入下一个环节。

内容的提问来源于stack exchange,提问作者Shubham Jain

火山引擎 最新活动