Getting started with Java’s ProcessBuilder: A simple utility class to interact with Linux from Java program


JDK 5.0 adds a new way of executing a command in a separate process, through a class called ProcessBuilder. You can find ProcessBuilder in the java.lang package (like Runtime and Process).

According to the docs,

This class is used to create operating system processes.Each ProcessBuilder instance manages a collection of process attributes. The start() method creates a new Process instance with those attributes. The start() method can be invoked repeatedly from the same instance to create new subprocesses with identical or related attributes.Each process builder manages these process attributes:* a command, a list of strings which signifies the external program file to be invoked and its arguments, if any. Which string lists represent a valid operating system command is system-dependent. For example, it is common for each conceptual argument to be an element in this list, but there are operating systems where programs are expected to tokenize command line strings themselves – on such a system a Java implementation might require commands to contain exactly two elements.
* an environment, which is a system-dependent mapping from variables to values. The initial value is a copy of the environment of the current process (see System.getenv()).
* a working directory. The default value is the current working directory of the current process, usually the directory named by the system property user.dir.
* a redirectErrorStream property. Initially, this property is false, meaning that the standard output and error output of a subprocess are sent to two separate streams, which can be accessed using the Process.getInputStream() and Process.getErrorStream() methods. If the value is set to true, the standard error is merged with the standard output. This makes it easier to correlate error messages with the corresponding output. In this case, the merged data can be read from the stream returned by Process.getInputStream(), while reading from the stream returned by Process.getErrorStream() will get an immediate end of file.

Have added here an utility class that you can make use of for interacting with Linux from Java programs.


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;

public class LinuxInteractor {

public static String executeCommand(String command, boolean waitForResponse) {

String response = "";

ProcessBuilder pb = new ProcessBuilder("bash", "-c", command);
pb.redirectErrorStream(true);

System.out.println("Linux command: " + command);

try {
Process shell = pb.start();

if (waitForResponse) {

// To capture output from the shell
InputStream shellIn = shell.getInputStream();

// Wait for the shell to finish and get the return code
int shellExitStatus = shell.waitFor();
System.out.println("Exit status" + shellExitStatus);

response = convertStreamToStr(shellIn);

shellIn.close();
}

}

catch (IOException e) {
System.out.println("Error occured while executing Linux command. Error Description: "
+ e.getMessage());
}

catch (InterruptedException e) {
System.out.println("Error occured while executing Linux command. Error Description: "
+ e.getMessage());
}

return response;
}

/*
* To convert the InputStream to String we use the Reader.read(char[]
* buffer) method. We iterate until the Reader return -1 which means
* there's no more data to read. We use the StringWriter class to
* produce the string.
*/

public static String convertStreamToStr(InputStream is) throws IOException {

if (is != null) {
Writer writer = new StringWriter();

char[] buffer = new char[1024];
try {
Reader reader = new BufferedReader(new InputStreamReader(is,
"UTF-8"));
int n;
while ((n = reader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
} finally {
is.close();
}
return writer.toString();
}
else {
return "";
}
}

}

If you want to start a process and exit so that the invoked process doesn’t get terminated),

LinuxInteractor.executeCommand(START_COMMAND, false);

If you want to get the status of something (say get the free memory details), you may need the response and only then you quit. In such cases,

LinuxInteractor.executeCommand(NORMAL_COMMAND, true);

4 comments

  1. Big thanks for sharing the util class to run shell commands.

  2. it’s …beautiful :’) this is exactly what the noob me tried to do but in the wrong way!
    so then would you mind explaining a bit more how the “response” is used?? I want to do an ad-hoc connection using this process builder.

  3. Hi ,
    Am trying to invoke a shell script using ProcessBuilder. i need to pass arguments to the shell script as below

    params = java.util.Arrays.asList(“./app/bin/testRunner.sh” “-n1” “-lTestScript” “-f./reports/snbc/”);
    ProcessBuilder processBuilder = new ProcessBuilder(params);

    Process process=processBuilder.start();

    the shell script “testRunner.sh”is not getting invoked , the issue is with the arguments with special character “-” hyphen. how do i go forward with this , any help ?

  4. Hey Deepak,

    It may have to do with you passing a list into the ProcessBuilder when it in fact probably wants comma seperated strings. If you print your list, does it include the necessary commas to seperate the strings?

Leave a comment