ProcessBuilder:转发stdout和stderr启动进程而不阻塞主线程

问题描述:

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

I'm building a process in Java using ProcessBuilder as follows:

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

InputStream stdOut = p.getInputStream();

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

Now my problem is the following: I would like to capture whatever is going through stdout and/or stderr of that process and redirect it to System.out asynchronously. I want the process and its output redirection to run in the background. So far, the only way I've found to do this is to manually spawn a new thread that will continuously read from stdOut and then call the appropriate write() method of 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();

虽然这种方法很有效,但感觉有点脏。最重要的是,它为我提供了一个正确管理和终止的线程。有没有更好的方法呢?

While that approach kind of works, it feels a bit dirty. And on top of that, it gives me one more thread to manage and terminate correctly. Is there any better way to do this?

只能在 Java 6或更早版本中使用使用所谓的 StreamGobbler (您开始创建):

To only way in Java 6 or earlier is with a so called StreamGobbler (which you are started to create):

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();
        }
    }
}

对于Java 7,请参阅Evgeniy Dorofeev的回答。

For Java 7, see Evgeniy Dorofeev's answer.