Как с помощью Java выполнить Shell-команду

Обзор

В этом руководстве мы рассмотрим два способа выполнения shell-команд из программы на Java . Первый способ – использовать класс Runtime и вызвать его метод exec. Второй (более гибкий способ) – создать экземпляр класса ProcessBuilder.

Зависимость операционной системы

Сначала нужно определить операционную систему, на которой работает наша JVM . В Windows необходимо запустить команду в качестве аргумента оболочки cmd.exe, а в остальных ОС мы будем использовать стандартную оболочку sh:

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

Ввод и вывод

Также нужно подключиться к входным и выходным потокам нашего процесса. По крайней мере, нужно получить выходные данные, иначе процесс зависнет.

Реализуем класс StreamGobbler, который использует 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);
    }
}

Примечание. Этот класс реализует интерфейс Runnable, а это означает, что он может быть выполнен любым исполнителем.

Runtime.exec()

Метод Runtime.exec() - это простой, но недостаточно гибкий способ создания нового подпроцесса.

В следующем примере мы запросим список пользователей из локальной директории и выведем его в консоли:

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;

ProcessBuilder

Класс ProcessBuilder является более гибким в использовании, чем Runtime. Он позволяет настраивать целый ряд параметров.

Например, можно:

  • изменить рабочий каталог, в котором работает shell-команда,
  • перенаправить потоки ввода и вывода;
  • наследовать их в потоках текущего процесса JVM, используя 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;

Заключение

Shell команды в Java можно выполнять двумя различными способами. Но если нужно настроить выполнение созданного процесса, то используйте класс ProcessBuilder.

Программные исходники примеров, приведенных в этой статье, доступны на GitHub.

Пожалуйста, опубликуйте ваши комментарии по текущей теме статьи. За комментарии, подписки, дизлайки, отклики, лайки огромное вам спасибо!

Пожалуйста, оставляйте ваши комментарии по текущей теме материала. За комментарии, отклики, подписки, дизлайки, лайки огромное вам спасибо!

Вадим Дворниковавтор-переводчик статьи «How to Run a Shell Command in Java»