Using Java ProcessBuilder to Execute a Piped Command

Viewed 30310

I'm trying to use Java's ProcessBuilder class to execute a command that has a pipe in it. For example:

ls -l | grep foo

However, I get an error:

ls: |: no such file or directory

Followed by:

ls: grep: no such file or directory

Even though that command works perfectly from the command line, I can not get ProcessBuilder to execute a command that redirects its output to another.

Is there any way to accomplish this?

3 Answers

Since Java 9, there’s genuine support for piplines in ProcessBuilder.

So you can use

List<String> result;
List<Process> processes = ProcessBuilder.startPipeline(List.of(
        new ProcessBuilder("ls", "-l")
            .inheritIO().redirectOutput(ProcessBuilder.Redirect.PIPE),
        new ProcessBuilder("grep", "foo")
            .redirectError(ProcessBuilder.Redirect.INHERIT)
    ));
try(Scanner s = new Scanner(processes.get(processes.size() - 1).getInputStream())) {
    result = s.useDelimiter("\\R").tokens().toList();
}

to get the matching lines in a list.

Or, for Windows

List<String> result;
List<Process> processes = ProcessBuilder.startPipeline(List.of(
        new ProcessBuilder("cmd", "/c", "dir")
            .inheritIO().redirectOutput(ProcessBuilder.Redirect.PIPE),
        new ProcessBuilder("find", "\"foo\"")
            .redirectError(ProcessBuilder.Redirect.INHERIT)
    ));
try(Scanner s = new Scanner(processes.get(processes.size() - 1).getInputStream())) {
    result = s.useDelimiter("\\R").tokens().toList();
}

These examples redirect stdin of the first process and all error streams to inherit, to use the same as the Java process.

You can also call .redirectOutput(ProcessBuilder.Redirect.INHERIT) on the ProcessBuilder of the last process, to print the results directly to the console (or wherever stdout has been redirected to).

Related