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

如何通过Apache Commons exec实现进程的动态交互式输入?

问题描述

我正尝试使用Apache Commons Exec实现与命令行进程的交互,但在代码中遇到了无法动态向进程传递命令的问题。以下是我的代码:

ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayOutputStream ins = new ByteArrayOutputStream();
OutputStreamWriter ow = new OutputStreamWriter(ins);
BufferedWriter writer = new BufferedWriter(ow);
ByteArrayInputStream in = new ByteArrayInputStream(ins.toByteArray());
PumpStreamHandler psh = new PumpStreamHandler(out, null, in);

CommandLine cl = CommandLine.parse(initProcess);
DefaultExecutor exec = new DefaultExecutor();
DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
exec.setStreamHandler(psh);

try {
    exec.execute(cl, resultHandler);
    int i = 0;
    while (true) {
        String o = out.toString();
        if (!o.trim().isEmpty()) {
            System.out.println(o);
            out.reset();
        }
        // --- PROBLEM start ---
        if (i == 3) {
            writer.write(internalProcessCommand); // 试过带或不带换行符都不行
            writer.flush();
            writer.close(); // 甚至试过直接用ins.write(internalProcessCommand.getBytes())
        }
        // --- PROBLEM end ---
        Thread.sleep(3000);
        i++;
    }
} catch (ExecuteException e) {
    System.err.println(e.getMessage());
}

我的逻辑是每隔3秒读取并打印进程输出流内容后清空流。当前核心问题在于:传递给PumpStreamHandler的输入流in无法支持动态写入命令——我需要像CLI交互那样持续向进程发送命令。当直接用System.in作为参数时可以正常从控制台输入,但通过代码传递字符串完全无效。

补充说明:

  • 我也曾尝试使用PipedInputStream和PipedOutputStream,但发现只有关闭PipedOutputStream后才能读取数据,无法复用,因此无法实现持续交互。

解决方案

问题的核心在于**ByteArrayInputStream是静态的**:一旦创建,它的内容就固定为创建时ins.toByteArray()的字节数组,后续写入ins的内容不会同步到in中,自然无法传递给进程。

针对这个问题,正确的做法是使用PipedInputStream和PipedOutputStream,并且避免提前关闭输出流,同时确保写入操作在独立线程中执行(因为管道流的读写操作会互相阻塞)。以下是修正后的实现:

// 初始化管道流:PipedOutputStream写入的数据会实时传递给PipedInputStream
PipedInputStream processInput = new PipedInputStream();
PipedOutputStream appOutput = new PipedOutputStream(processInput);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(appOutput));

ByteArrayOutputStream processOutput = new ByteArrayOutputStream();
PumpStreamHandler psh = new PumpStreamHandler(processOutput, null, processInput);

CommandLine cl = CommandLine.parse(initProcess);
DefaultExecutor exec = new DefaultExecutor();
DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
exec.setStreamHandler(psh);

try {
    exec.execute(cl, resultHandler);
    int i = 0;
    while (true) {
        // 读取并打印进程输出
        String output = processOutput.toString();
        if (!output.trim().isEmpty()) {
            System.out.println(output);
            processOutput.reset();
        }

        // 动态向进程发送命令
        if (i == 3) {
            writer.write(internalProcessCommand);
            writer.newLine(); // 注意:大多数交互式进程需要换行符来确认命令
            writer.flush(); // 必须flush才能让数据立即进入管道
            // 不要关闭writer/appOutput,否则后续无法继续写入
        }

        Thread.sleep(3000);
        i++;
    }
} catch (Exception e) {
    System.err.println(e.getMessage());
} finally {
    // 程序结束时再关闭资源
    try {
        writer.close();
        appOutput.close();
        processInput.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

关键改进点:

  1. 使用PipedInputStream和PipedOutputStream替代静态的ByteArrayInputStream,实现数据的实时传递。
  2. 不提前关闭输出流,保持管道打开以支持持续写入。
  3. 向进程发送命令时添加换行符(writer.newLine()),因为大多数交互式CLI进程需要换行来识别命令输入完成。
  4. 确保每次写入后调用flush(),让数据立即进入管道供进程读取。

另外需要注意:如果你的写入操作比较频繁或者数据量较大,建议把写入逻辑放到单独的线程中执行,避免主线程被管道的阻塞操作卡住。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:18:51