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

通过BufferedReader获取SSH输出时远程命令挂起问题排查

兄弟,这个问题我踩过好几次坑了,本质上是Java处理子进程IO的机制导致的,咱们来捋清楚:

为什么会挂起?

  • IO缓冲区阻塞:Java通过Runtime.exec()启动的子进程,有自己的标准输出(stdout)和错误输出(stderr)缓冲区。如果这些缓冲区被写满,子进程就会暂停执行,等着Java这边把缓冲区的内容读走释放空间。你执行的ssh远程命令,哪怕输出不多,但如果有错误输出(比如ssh的一些警告信息)没被读取,缓冲区满了就会卡住。
  • 隐藏的交互式请求:虽然你在命令行执行没问题,但Java的子进程不会继承终端的交互环境。比如如果是第一次连接目标主机,ssh会弹出确认主机密钥的提示,但Java进程没法回应这个交互,直接就挂住了;或者如果你的ssh需要密码,但命令行用了ssh-agent记住密码,Java进程没继承这个环境也会卡住。

解决方法

1. 必须处理所有输出流(最关键)

一定要同时读取stdout和stderr,最好用单独的线程分别处理,避免主线程阻塞。这里给你一段可用的代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class SshCommand {
    public static void main(String[] args) throws IOException, InterruptedException {
        Runtime rt = Runtime.getRuntime();
        Process process = rt.exec("ssh root@192.168.0.1 cat a.txt");

        // 单独线程读标准输出
        new Thread(() -> {
            try (BufferedReader stdoutReader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = stdoutReader.readLine()) != null) {
                    System.out.println("远程输出:" + line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();

        // 单独线程读错误输出
        new Thread(() -> {
            try (BufferedReader stderrReader = new BufferedReader(
                    new InputStreamReader(process.getErrorStream()))) {
                String line;
                while ((line = stderrReader.readLine()) != null) {
                    System.err.println("错误信息:" + line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();

        // 等待命令执行完成
        int exitCode = process.waitFor();
        System.out.println("命令执行结束,退出码:" + exitCode);
    }
}

2. 用ProcessBuilder简化流处理

ProcessBuilder比Runtime.exec()更灵活,可以把stderr合并到stdout,这样只需要处理一个流,代码更简洁:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class SshProcessBuilder {
    public static void main(String[] args) throws IOException, InterruptedException {
        ProcessBuilder pb = new ProcessBuilder(
                "ssh", "root@192.168.0.1", "cat a.txt");
        pb.redirectErrorStream(true); // 合并stderr到stdout

        Process process = pb.start();
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(process.getInputStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        }

        int exitCode = process.waitFor();
        System.out.println("退出码:" + exitCode);
    }
}

3. 消除ssh的交互式请求

  • 如果是主机密钥验证导致的:可以在ssh命令中添加-o StrictHostKeyChecking=no参数(测试环境用,生产环境不建议,最好提前把目标主机加到Java进程用户的~/.ssh/known_hosts里),命令变成:
    ssh -o StrictHostKeyChecking=no root@192.168.0.1 cat a.txt
    
  • 如果是需要密码的情况:建议配置密钥免密登录(把本地公钥放到目标主机的~/.ssh/authorized_keys里),这样ssh不需要密码就能连接;如果非要用密码,可以用sshpass工具(需要先安装),命令变成sshpass -p '你的密码' ssh root@192.168.0.1 cat a.txt,但密码明文写在代码里不安全,谨慎使用。

内容的提问来源于stack exchange,提问作者Shay Gold

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 03:38:20