Search code examples
javaioprocessbuilder

InheritIO and redirecting stdout


I'm trying to write a test for a class that uses ProcessBuilder.inheritIO and I can't seem to get it to work. I've stripped the code down to the core and came up with the following example that I think should write all the output of the child process to a ByteArrayOutputStream but doesn't.

Environment: Java 9.0.4 on Windows 7

import java.io.*;

public class ProcessTest {
  public static void main(String[] args) throws Throwable {
    PrintStream original = System.out;
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    PrintStream ps = new PrintStream(baos);
    System.setOut(ps);

    System.out.println("BEGIN");
    Process process = new ProcessBuilder().command("where", "where")
                                          .inheritIO().start();
    process.waitFor();

    System.out.println("END");
    String output = new String(baos.toByteArray());
    original.println("output = " + output);
  }
}

The output is:

C:\Windows\System32\where.exe
output = BEGIN
END

And when I look in the debugger, the output stream does not contain the path to where.

I'm thinking System.setOut isn't doing what I'm thinking but I'm not sure. I found other SO questions suggest a StreamGrabber but I thought that was for Java 6 and earlier and inheritIO would work for me. Any help would be appreciated. Thanks.

Edit I've come to realize that calling System.setOut() isn't really doing what I hope. It is not changing the underlying file descriptor of 1. If I was doing this in C, I would be working with the low level file descriptors. But that's not what setOut() is doing. That's just changing pointers.


Solution

  • System.setOut changes the Java process’s standard output. There’s no guarantee it will affect child processes.

    Regardless, it’s probably not a good idea to alter a global setting just to capture the output of a single process, especially if you expect your code to run amidst other other code (libraries, appications, etc.).

    Solution: Don’t try to hack System.out. Read from the Process’s stdout InputStream:

    ProcessBuilder builder = new ProcessBuilder().command("where", "where").inheritIO();
    builder.redirectOutput(ProcessBuilder.Redirect.PIPE);
    Process process = builder.start();
    
    String output;
    try (InputStream processStdOut = process.getInputStream()) {
        output = new String(processStdOut.readAllBytes());
    }
    
    process.waitFor();