How to Run a Shell Command in Java

1. Overview

With this tutorial we’ll illustrate the two ways of executing a shell command from within Java code.

The first is to use the Runtime class and call its exec method.

The second and more customizable way, will be to create and use a ProcessBuilder instance.

2. Operating System Dependency

Before we’re going to create a new Process executing our shell command, we need to first determine the operating system on which our JVM is running.

That’s because, on Windows, we need to run our command as argument to the cmd.exe shell and on all other operating systems we can issue a standard shell, called sh:

boolean isWindows = System.getProperty("os.name")
  .toLowerCase().startsWith("windows");

3. Input and Output

Furthermore ** we need a way to hook into the input and output streams of our process.

At least the output must be consumed – otherwise our process doesn’t return successfully, instead it will hang.

Let’s implement a commonly used class called StreamGobbler which consumes an InputStream:

private static class StreamGobbler implements Runnable {
    private InputStream inputStream;
    private Consumer<String> consumer;

    public StreamGobbler(InputStream inputStream, Consumer<String> consumer) {
        this.inputStream = inputStream;
        this.consumer = consumer;
    }

    @Override
    public void run() {
        new BufferedReader(new InputStreamReader(inputStream)).lines()
          .forEach(consumer);
    }
}

NOTE: This class is implementing the Runnable interface, which means that it could be executed by any Executor.

4. Runtime.exec()

A method-call to Runtime.exec() is a simple, not yet customizable, way to spawn a new sub-process.

In the following example we will request a directory-listing of a users home-directory and printing it to the console:

String homeDirectory = System.getProperty("user.home");
Process process;
if (isWindows) {
    process = Runtime.getRuntime()
      .exec(String.format("cmd.exe /c dir %s", homeDirectory));
} else {
    process = Runtime.getRuntime()
      .exec(String.format("sh -c ls %s", homeDirectory));
}
StreamGobbler streamGobbler =
  new StreamGobbler(process.getInputStream(), System.out::println);
Executors.newSingleThreadExecutor().submit(streamGobbler);
int exitCode = process.waitFor();
assert exitCode == 0;

5. ProcessBuilder

For the second implementation of our computing problem, we’ll be using a ProcessBuilder. This is preferred over the Runtime approach because we’re able to customize some details.

For example we’re able to:

  • change the working directory our shell command is running in using builder.directory()

  • set-up a custom key-value map as environment using builder.environment()

  • redirect input and output streams to custom replacements

  • inherit both of them to the streams of the current JVM process using builder.inheritIO()

ProcessBuilder builder = new ProcessBuilder();
if (isWindows) {
    builder.command("cmd.exe", "/c", "dir");
} else {
    builder.command("sh", "-c", "ls");
}
builder.directory(new File(System.getProperty("user.home")));
Process process = builder.start();
StreamGobbler streamGobbler =
  new StreamGobbler(process.getInputStream(), System.out::println);
Executors.newSingleThreadExecutor().submit(streamGobbler);
int exitCode = process.waitFor();
assert exitCode == 0;

6. Conclusion

As we’ve seen in this quick tutorial, we can execute a shell command in Java in two distinct ways.

Generally, if you’re planning to customize the execution of the spawned process, for example, to change its working directory, you should consider using a ProcessBuilder.

As always, you’ll find the sources on GitHub.

Leave a Reply

Your email address will not be published.