Home > Net >  Parse Output of Child Process
Parse Output of Child Process

Time:11-16

In Java, I am launching a child process like so:

String[] command = {"java", "-jar", jarPath};
Process childProcess = new ProcessBuilder().inheritIO().command(command).start();

I expect the child process to print Success! to standard output at some point (if things are successful). How do I periodically (e.g., every 5 seconds) check if there's a Success! line in the output that is created by the child process?

CodePudding user response:

If you want to perform IO with the subprocess, don’t use inheritIO. Instead, capture the process’ output stream via getInputStream() and read it via a BufferedReader:

final Process childProcess = new ProcessBuilder().command(command).start();
try (final BufferedReader br = new BufferedReader(new InputStreamReader(childProcess.getInputStream()))) {
    String line;

    while ((line = br.readLine()) != null) {
        if (line.equals("Success!")) {
            System.out.println("Successfully completed");
            // Maybe, depending on whether you need the remaining output:
            break;
        } else {
            // Handle output from process; e.g.:
            System.out.println("No success yet.");
        }
    }
}

// At this point, the child process has not yet necessarily completed!
childProcess.waitFor();

Importantly, you don’t need to check for output periodically: the loop performs synchronous IO, that is, it will automatically wait until there’s output available from the child process. All you need to do is check whether the output is what you expect.

CodePudding user response:

So... this is rather a loaded question, although that might not be obvious from just asking it.

I was not able to do this using the ProcessBuilder class
(maybe someone with more experience can answer using that route).

Here is what you need to do: use the class Runtime.getRuntime().exec(command) to run your command. This gives you easy access to the standard input and standard error streams.

Below is a general complete class you can use.

Usage:

    final String jarPath = "jarPath.jar";
    final String[] command = { "java.exe", "-jar", jarPath };

    final CrossPlatformCommand childProcess = new CrossPlatformCommand();
    final int exitCode = childProcess.execute(command[0]   " "   command[1]   " "   command[2]);
    System.out.println("Parse child process output: "   childProcess.getInput());

Output:

Parse child process output: Success!

There are a few caveats with this: the dollar sign needs to be escaped in Linux. There may be other characters that need to be escaped (this has not been investigated fully, and you may need to modify this class if you are using other characters that need to be escaped.)

 public class CrossPlatformCommand
 {
    private static final String SHELL_LINUX = "/bin/bash";
 
    private static final int BUFFER_SIZE = 4096;
 
    private static final String OS_NAME = System.getProperty("os.name").toLowerCase(Locale.getDefault());
 
    private final StringBuffer error = new StringBuffer();
 
    private final StringBuffer input = new StringBuffer();
 
    public int execute(final String command) throws Exception
    {
        final int exitCode;
 
        if (OS_NAME.startsWith("windows"))
        {
            exitCode = executeWindows(command);
        }
        else
        {
            exitCode = executeLinux(command); // OS_NAME.startsWith("linux")
        }
 
        return exitCode;
    }
 
    public String getError()
    {
        return this.error.toString();
    }
 
    public String getInput()
    {
        return this.input.toString();
    }
 
    private int executeLinux(final String command) throws Exception
    {
        final Process proc = Runtime.getRuntime().exec(SHELL_LINUX);
        processLinuxCommand(proc, command);
        return processStreams(proc);
    }
 
    private int executeWindows(final String command) throws Exception
    {
        final Process proc = Runtime.getRuntime().exec(command);
        return processStreams(proc);
    }
 
    private static void processLinuxCommand(final Process proc, final String command) throws Exception
    {
        try (BufferedWriter out = new BufferedWriter(new OutputStreamWriter(proc.getOutputStream())))
        {
            // Dollar signs are special in Linux.
            out.write(command.replace("$", "\\$"));
 
            out.newLine();
            out.flush();
        }
    }
 
    private int processStreams(final Process proc) throws Exception
    {
        fillBuffer(proc.getInputStream(), this.input);
        fillBuffer(proc.getErrorStream(), this.error);
        return proc.waitFor();
    }
 
    private static void fillBuffer(final InputStream in, final StringBuffer sb) throws IOException
    {
        sb.setLength(0);
 
        final BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        final char[] buffer = new char[BUFFER_SIZE];
        final int length = reader.read(buffer);
        for (int i = 0; i < length; i  )
        {
            sb.append(buffer[i]);
        }
    }
 }
 
  • Related