11

In other languages (like bash and Python), when we spawn a child process, this new process will inherit the stdout and stderr from the parent. This means that any output from the child process will be printed to the terminal as well as the output from the parent.

How can we achieve the same behavior in Java?

My first try was:

proc = Runtime.getRuntime().exec(cmd);

But it won't work. Based on this answer and this answer, I've replaced the code with:

ProcessBuilder pb = new ProcessBuilder(cmd);
pb.redirectOutput(System.out);
pb.redirectError(System.err);

But this doesn't even compile, as the arguments are incompatible with the expected method parameters.

Community
  • 1
  • 1
Denilson Sá Maia
  • 47,466
  • 33
  • 109
  • 111

3 Answers3

18

You need to read the output and error streams of the process you've created and write the output to where you want it to go (in your case System.out and System.err).

Edit. The above answer is correct for java < 7. As of 7 from the javadoc it looks to be possible to instead call

processBuilder.redirectOutput(Redirect.INHERIT)
henry
  • 5,923
  • 29
  • 46
  • 1
    You mean that I need to implement a loop that just keeps reading from one stream and writing in another one? Sorry, not feasible. Too much work (probably requires running in a new thread, and it introduces lots of points of possible bugs). – Denilson Sá Maia May 10 '12 at 19:22
  • 3
    Feasible or not this what used to be required. I've just checked the java 7 api and it looks like it's been addressed - you can now call redirectOutput passing in Redirect.INHERIT which looks to achieve what you want. – henry May 10 '12 at 19:31
3

This isn't too bad. I initially said "easy", then I realized I'd had to code the InputStreamConsumer:

public static class InputStreamConsumer extends Thread {

    private InputStream is;

    public InputStreamConsumer(InputStream is) {
        this.is = is;
    }

    @Override
    public void run() {

        try {
            int value = -1;
            while ((value = is.read()) != -1) {
                System.out.print((char)value);
            }
        } catch (IOException exp) {
            exp.printStackTrace();
        }
    }
}

private void captureOutput(Process p) {

    InputStreamConsumer stdout;
    InputStreamConsumer errout;

    errout = new InputStreamConsumer(p.getErrorStream());
    stdout = new InputStreamConsumer(p.getInputStream());
    errout.start();
    stdout.start();
}

....running inside of something like ...

    ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/D", "/C", myCommand, parm, parm...);
    try {
        System.out.println("Start "+myCommand);
        Process myProcess = pb.start();
        captureOutput(myProcess);

        int returnCode = myProcess.waitFor();
        System.out.println("myProcess: return code : "+returnCode);
    }
    catch (IOException e) {
        e.printStackTrace();
    }
  • The others didn't work for me, except in the IDE. This one did, thank you. – Steve Apr 06 '15 at 21:08
  • 1
    Looks nice - but do we have to use such self-implemented code? There is no support in Java to redirect STDERR into STDOUT and then to read the combination of them from one input-stream? – SomethingSomething Apr 08 '15 at 11:55
1

It's certainly possible. The Ant Java task does this when fork is set true. I don't remember all the details, but on a quick look it appears that most of the magic is in PumpStreamHandler. You might try finding some inspiration from that.

asudell
  • 101
  • 2
  • 1
    Actually easier than that, but only applicable in 1.7, which you seem to be using since you're using ProcessBuilder.redirectOutput() is to do pb.redirectOutput(Redirect.INHERIT). Though I've never done that myself. – asudell May 10 '12 at 19:25