Search code examples
javaexecfile-descriptor

Execute a command in Java without redirecting the output


How do I run an external command (via a shell) from a Java program, such that no redirection takes place, and wait for the command to end? I want the file descriptors of the external program to be the same as those of the Java program. In particular I do not want the output to be redirected to a pipe that the Java program is reading. Having the Java program relay the output is not a solution.

This means that a plain invocation of java.lang.Runtime.exec is not the solution. I presume that java.lang.ProcessBuilder is involved, but how do I specify that output and error streams must be the same as the calling Java process?

class A {
    public static void main(String[] args) {
        try {
            ProcessBuilder pb = new ProcessBuilder("echo", "foo");
            /*TODO: pb.out = System.out; pb.err = System.err;*/
            Process p = pb.start();
            p.waitFor();
        } catch (Exception e) {
            System.err.println(e);
            System.exit(1);
        }
    }
}

(This may or may not be the right approach.)

In other words, I'm looking for Java's system, but all I can find is (roughly) popen.

Here's an example of a situation where relaying cannot work: if the subprocess writes to both stdout and stderr and the Java program is relaying, then the Java program has no way to know the order of the write calls in the subprocess. So the order of the output on stdout and stderr from the Java program will be observably different if the two streams end up in the same file. Mixing stdout and stderr is of course not a solution because the caller may want to keep them separate.

While I think this question is of general interest, a Linux-specific solution would solve my immediate problem.


Solution

  • This is the intent of ProcessBuilder.redirectError/redirectOutput which were introduced in Java 7. Using Redirect.INHERIT will make the child process share stderr/stdout with the Java process:

    class A {
        public static void main(String[] args) {
            try {
                ProcessBuilder builder = new ProcessBuilder("echo", "foo");
                builder.redirectError(ProcessBuilder.Redirect.INHERIT);
                builder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
                Process p = builder.start();
                p.waitFor();
            } catch (Exception e) {
                System.err.println(e);
                System.exit(1);
            }
        }
    }