Question

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.

Was it helpful?

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

OTHER TIPS

You might take a look at the NuProcess project. Disclaimer: I wrote it. It provides non-blocking I/O from spawned processes. You still have to relay in Java (you receive callbacks), but because it is using epoll() in the case of Linux, I would expect it to preserve the order of the underlying program. Only a single thread is epoll()'ing the pipes so you won't get any thread scheduling order issues.

I'm 100% order would be preserved on MacOS X, or any BSD variant, because it uses a kqueue which is definitely ordered. Anyway, you might want to give it a shot, it's trivial to code and test.

You can't. By default all standard I/O of the child process are redirected to the parent process (the jvm running your java program).

from the javadoc of the Process class:

By default, the created subprocess does not have its own terminal or console. All its standard I/O (i.e. stdin, stdout, stderr) operations will be redirected to the parent process, where they can be accessed via the streams obtained using the methods getOutputStream(), getInputStream(), and getErrorStream(). The parent process uses these streams to feed input to and get output from the subprocess. Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, or even deadlock.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top