ProcessBuilder in Java

Naveen AnamaneniUpdated · 4 min read
mediumJava 25javautilityprocess

The ProcessBuilder class in Java, in java.lang, is used to start an external program, such as a script, a command-line tool or another Java program, from inside your Java program. You give it the command and its arguments, and optionally a working directory and environment variables; it then starts a Process whose output you can read and whose exit code you can check. In this lesson, you will learn how to run a command, read its output, and handle errors.

Declaration

Declaration
public final class ProcessBuilder

Methods

MethodDescription
new ProcessBuilder("cmd", "arg1", ...)It is used to set the program and its arguments.
directory(File dir)It sets the working directory.
environment()It returns a modifiable map of environment variables.
redirectErrorStream(true)It merges error output into normal output.
redirectOutput(File f) / inheritIO()It sends output to a file, or to this program's console.
start()It starts the process and returns a Process.
process.getInputStream()It reads what the process prints.
process.waitFor()It waits for the process to finish and returns its exit code.
process.waitFor(timeout, unit)It waits at most a given time.

Example: Running a Command and Reading Its Output

Let's see an example that runs the echo command, which exists on macOS and Linux. On Windows, the command would be new ProcessBuilder("cmd", "/c", "echo", ...).

RunEcho.java▶ Try it
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
public class RunEcho {
    public static void main(String[] args)
            throws IOException, InterruptedException {
        ProcessBuilder pb = new ProcessBuilder("echo", "Hello from Navika");
        pb.redirectErrorStream(true);
        Process process = pb.start();
 
        try (BufferedReader out = new BufferedReader(
                new InputStreamReader(process.getInputStream()))) {
            String line;
            while ((line = out.readLine()) != null) {
                System.out.println("child said: " + line);
            }
        }
        int exitCode = process.waitFor();
        System.out.println("exit code: " + exitCode);
    }
}
Output
child said: Hello from Navika
exit code: 0

Explanation: Each part of the command is a separate argument; no shell is involved, so spaces inside "Hello from Navika" are kept as one argument. start() launches the program. Its output arrives through getInputStream() (it's "input" from Java's point of view). waitFor() returns the exit code; 0 means success.

Example: Exit Codes and Errors

ExitCodes.java▶ Try it
import java.io.IOException;
 
public class ExitCodes {
    public static void main(String[] args) throws InterruptedException {
        try {
            Process ok = new ProcessBuilder("true").start();
            System.out.println("true exited with " + ok.waitFor());
 
            Process fail = new ProcessBuilder("false").start();
            System.out.println("false exited with " + fail.waitFor());
 
            new ProcessBuilder("no-such-program-xyz").start();
        } catch (IOException e) {
            System.out.println("Could not start: "
                + e.getClass().getSimpleName());
        }
    }
}
Output
true exited with 0
false exited with 1
Could not start: IOException

Explanation: The Unix commands true and false do nothing except exit with 0 and 1. A program that doesn't exist can't be started, so start() throws IOException.

Security Note

Never build a command by joining user input into one string and running it through a shell (sh -c "..."). A user could add ; rm -rf ... to your command. Pass each argument separately to ProcessBuilder, which doesn't use a shell, and validate input first.

Common Mistakes

Passing the whole command as one string: new ProcessBuilder("ls -l"). It looks for a program literally named "ls -l"; split it: ("ls", "-l").

Not reading the output of a chatty process. It can block forever.

Ignoring the exit code. A non-zero code means the program failed.

Practice

  1. Easy: Run java --version with ProcessBuilder and print every line it outputs.
  2. Harder: Run a command with waitFor(2, TimeUnit.SECONDS), and call destroy() on the process if it hasn't finished in time.

Quick check: ProcessBuilder

4 questions
1What does start() return?
2How do you read what the child program prints?
3What does exit code 0 usually mean?
4Why pass arguments separately?

Key Takeaways

Was this tutorial helpful?

Lesson 340 of 482