Как с помощью 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.

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