开发者

Using Java ProcessBuilder to Execute a Piped Command

开发者 https://www.devze.com 2023-01-17 11:24 出处:网络
I\'m trying to use Java\'s ProcessBuilder class to execute a command that has a pipe in it.For example:

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 开发者_JAVA技巧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?


This should work:

ProcessBuilder b = new ProcessBuilder("/bin/sh", "-c", "ls -l| grep foo");

To execute a pipeline, you have to invoke a shell, and then run your commands inside that shell.


The simplest way is to invoke the shell with the command line as the parameter. After all, it's the shell which is interpreting "|" to mean "pipe the data between two processes".

Alternatively, you could launch each process separately, and read from the standard output of "ls -l", writing the data to the standard input of "grep" in your example.


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).

0

精彩评论

暂无评论...
验证码 换一张
取 消