ProcessBuilder:转发已启动进程的标准输出和 stderr,而不会阻塞主线程

2022-08-31 10:03:55

我正在使用ProcessBuilder在Java中构建一个流程,如下所示:

ProcessBuilder pb = new ProcessBuilder()
        .command("somecommand", "arg1", "arg2")
        .redirectErrorStream(true);
Process p = pb.start();

InputStream stdOut = p.getInputStream();

现在我的问题是:我想捕获通过该过程的stdout和/或stderr的任何内容,并将其重定向到异步。我希望进程及其输出重定向在后台运行。到目前为止,我发现这样做的唯一方法是手动生成一个新线程,该线程将不断读取,然后调用适当的方法。System.outstdOutwrite()System.out

new Thread(new Runnable(){
    public void run(){
        byte[] buffer = new byte[8192];
        int len = -1;
        while((len = stdOut.read(buffer)) > 0){
            System.out.write(buffer, 0, len);
        }
    }
}).start();

虽然这种方法有点有效,但感觉有点脏。最重要的是,它又给了我一个线程来正确管理和终止。有没有更好的方法来做到这一点?


答案 1

使用 ,它将子进程标准 I/O 的源和目标设置为与当前 Java 进程的源和目标相同。ProcessBuilder.inheritIO

Process p = new ProcessBuilder().inheritIO().command("command1").start();

如果 Java 7 不是一个选项

public static void main(String[] args) throws Exception {
    Process p = Runtime.getRuntime().exec("cmd /c dir");
    inheritIO(p.getInputStream(), System.out);
    inheritIO(p.getErrorStream(), System.err);

}

private static void inheritIO(final InputStream src, final PrintStream dest) {
    new Thread(new Runnable() {
        public void run() {
            Scanner sc = new Scanner(src);
            while (sc.hasNextLine()) {
                dest.println(sc.nextLine());
            }
        }
    }).start();
}

当子进程完成时,线程将自动失效,因为 EOF 将如此。src


答案 2

对于Java 7及更高版本,请参阅Evgeniy Dorofeev的答案

对于 Java 6 及更早版本,请创建并使用 :StreamGobbler

StreamGobbler errorGobbler = 
  new StreamGobbler(p.getErrorStream(), "ERROR");

// any output?
StreamGobbler outputGobbler = 
  new StreamGobbler(p.getInputStream(), "OUTPUT");

// start gobblers
outputGobbler.start();
errorGobbler.start();

...

private class StreamGobbler extends Thread {
    InputStream is;
    String type;

    private StreamGobbler(InputStream is, String type) {
        this.is = is;
        this.type = type;
    }

    @Override
    public void run() {
        try {
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            String line = null;
            while ((line = br.readLine()) != null)
                System.out.println(type + "> " + line);
        }
        catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }
}

推荐