piping output of ProcessBuilder to another ProcessBuilder
Asked Answered
S

2

10

Is it possible to pass the output of one process created by ProcessBuilder to another process created by another ProcessBuilder? For example, if I'm trying to execute this shell command:

ls | grep build.xml

how should I do it with ProcessBuilder?

as @erdinc suggested, I tried this:

Process process = Runtime.getRuntime().exec("ls");
InputStream is = process.getInputStream();
byte[] buf = new byte[1000];
is.read(buf);
String parameter = new String(buf);
System.out.println("grep build " + parameter);

Process proc2 = Runtime.getRuntime().exec("grep build " + parameter);
InputStream is2 = proc2.getInputStream();
byte[] buf2 = new byte[1000];
is2.read(buf2);
String result = new String(buf2);
System.out.println("proc2 result: " + result);

but it produces different result compare to when I run the script directly in the shell. Where did I do wrong?

Solved: Please see Philipp Wendler solution

Siding answered 5/1, 2012 at 20:44 Comment(0)
P
6

The problem with the solution you have is that it reads only the first 1000 bytes from the output of ls and passes them to grep. As you don't know the amount of output from ls before, you need to read it iteratively until it is exhausted.

Here is a solution using BufferedReader, which will send each line of output to grep:

Process lsProcess = Runtime.getRuntime().exec("ls");
BufferedReader lsOutput = new BufferedReader(new InputStreamReader(lsProcess.getInputStream()));
Process grepProcess = Runtime.getRuntime().exec("grep build.xml");
BufferedWriter grepInput = new BufferedWriter(new OutputStreamWriter(grepProcess.getOutputStream()));

String line;
// read each line from ls until there are no more
while ((line = lsOutput.readLine()) != null) {
    // and send them to grep
    grepInput.write(line);
    grepInput.newLine();
}

// send end-of-file signal to grep so it will terminate itself
grepInput.close();

Of course you need to add the appropriate error-handling here. You can omit the Reader and Writer and pass byte[] arrays directly from the input to the output stream if this solution is too slow.

However, a much better solution would be not to use ls and grep to look for a file in the filesystem, but to use the appropriate Java API. For example if you create a File object for the directory you are interested in, you can use the various listFiles methods on it to list all files in this directory. This method is much easier, portable, less error-prone and probably faster.

Purdy answered 8/1, 2012 at 10:53 Comment(1)
your solution works! thank you Philipp :D I'm actually will be using this code to call several external application such as chasen and moses (machine translation tools). The ls | grep example was meant only to simplify the question, but thanks for the suggestion :D I'll mark the question solved and upvote your solution.Siding
M
0

You can use getInputStream method than pass as parameter second Process. Example code;

        process = Runtime.getRuntime().exec("ls");
        InputStream is = process.getInputStream();
        byte[] buf = new byte[1000];
        is.read(buf);
        String parameter = new String(buf);
        System.out.println(parameter);
        Runtime.getRuntime().exec("grep build.xml " + parameter);
Mckenna answered 5/1, 2012 at 21:31 Comment(2)
but how to pass the "ls" input stream to "grep" process? I can't seem to find method setInputStream in Process. Also can it be done with ProcessBuilder?Siding
I've tried your solution @erdinc, but it produces different result compare to running the command directly in shell. I've edited the question to show what I've done. FYI, I'm not trying to run grep with ls result as the parameter, but I'm trying to pipe the result of ls to the grep process.Siding

© 2022 - 2024 — McMap. All rights reserved.