ProcessBuilder BufferedReader read()阻塞

时间:2017-02-05 14:56:54

标签: java bufferedreader processbuilder

我正在使用ProcessBuilder来运行我们正在使用的命令行工具。在运行过程中,工具会询问2是/否问题,所以通常我会回答'y'两次,然后每次按回车键。我的问题是该工具总是在从cmd运行时完成运行,但是当我通过我的java代码运行它时,它有时会工作,有时会卡在while ((n = op.read(buffer)) != -1)上(使用相同的输入)。

这是我的代码。难道我做错了什么?我错过了什么?感谢。

List<String> processArgs = new ArrayList<>();
processArgs.add(0, "java");
processArgs.add(1, "-jar");
processArgs.add(2, JAR_PATH);
processArgs.add(3, "-put");
processArgs.addAll(args);
try
{
    // run tool with put
    ProcessBuilder pb = new ProcessBuilder(processArgs);
    pb.directory(new File("src\\temp"));
    pb.redirectErrorStream(true);
    Process p = pb.start();

    // write 'y' to the tool's stdin.
    String answer = "y" + System.getProperty("line.separator");
    // yes to first question
    p.getOutputStream().write(answer.getBytes());
    p.getOutputStream().flush();

    // read tool's process stdout
    this.op = new BufferedReader(new InputStreamReader(p.getInputStream()));
    StringWriter sw = new StringWriter();
    int n = 0;
    boolean answered = false;
    char[] buffer = new char[BUFFER_SIZE];
    while ((n = op.read(buffer)) != -1)
    {
        sw.write(buffer, 0, n);
        if (sw.toString().contains("second question") && !answered)
        {
            // yes to second question
            p.getOutputStream().write(answer.getBytes());
            p.getOutputStream().flush();
            answered = true;
        }
    }

    stdout = sw.toString();
    exitCode = p.waitFor();
}
catch (IOException | InterruptedException e)
{
    throw new ToolException("process had an exception:\n" + e.getMessage());
}

更新: 我更改了代码并添加了pb.redirectErrorStream(true),但现在该进程仍然在op.read(buffer)上被阻止。当我调试时,它似乎停留在第二个问题上,即使我在输出流中写了两次'y'。我错误地使用getOutputStream()吗?

第二次更新: 第二个问题没有得到第二个'y'作为答案,它导致过程等待输入。我更改了代码,因此我将显示将输入插入子进程输出流的正确方法。

1 个答案:

答案 0 :(得分:2)

缓冲输出和错误流。当缓冲区填满时,程序停止等待您阅读。但是,您只能首先读取输出,因此如果错误流填充,则会出现死锁。

一个简单的解决方案是将错误重定向到输出,这样您只能读取一个流。即。

pb.redirectErrorStream(true);

根据文档https://docs.oracle.com/javase/8/docs/api/java/lang/ProcessBuilder.html

替代品包括;将错误写入文件,或在另一个线程中读取它。