Рубрики
Без рубрики

Выполнение команд оболочки с помощью Java

В этом уроке мы рассмотрим, как выполнять команды оболочки, файлы bat и sh на Java. Мы рассмотрим примеры для всех подходов exec() и ProcessBuilder.

Автор оригинала: David Landup.

Вступление

В этой статье мы рассмотрим, как мы можем использовать классы Runtime и ProcessBuilder для выполнения команд и сценариев оболочки с помощью Java.

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

Этот процесс также созрел для автоматизации. Нет необходимости запускать все вручную. Используя Java, мы можем запускать одну или несколько команд оболочки, выполнять сценарии оболочки, запускать терминал/командную строку, устанавливать рабочие каталоги и управлять переменными среды с помощью основных классов.

Время выполнения.exec()

Класс Runtime в Java-это класс высокого уровня, присутствующий в каждом отдельном приложении Java. Через него само приложение взаимодействует с окружающей средой, в которой оно находится.

Извлекая среду выполнения, связанную с нашим приложением, с помощью метода getRuntime () , мы можем использовать метод exec() для прямого выполнения команд или запуска файлов .bat /|. sh .

Метод exec() предлагает несколько перегруженных вариантов:

  • публичный процесс exec(строковая команда) – Выполняет команду, содержащуюся в command , в отдельном процессе.
  • публичный процесс exec(строковая команда, Строка[] envp) – Выполняет команду | с массивом переменных среды. Они представлены в виде массива строк, следующих за форматом имя=значение . публичный процесс exec(Строковая команда, Строка[] envp, директория файла)
  • - Выполняет команду с указанными переменными среды из каталога | каталога. публичный процесс exec(Строка cmdArray[]) – Выполняет команду в виде массива строк.
  • публичный процесс exec(Строка cmdArray[], Строка[] envp) – Выполняет команду с указанными переменными среды.
  • публичный процесс exec(Строка cmdarray[], Строка[] envp, файл dir) – Выполняет команду с указанными переменными среды из каталога
  • dir .

Стоит отметить, что эти процессы запускаются извне из интерпретатора и будут зависеть от системы.

Что также стоит отметить, так это разницу между Строковой командой и Строковой командой cmdArray[] . Они достигают одного и того же. Команда | в любом случае разбивается на массив, поэтому использование любого из этих двух должно привести к одинаковым результатам.

Вам решать, хотите ли вы использовать exec("директория/папка") или exec(новая строка[]{"директория", "/папка"} .

Давайте приведем несколько примеров, чтобы увидеть, чем эти перегруженные методы отличаются друг от друга.

Выполнение команды из строки

Давайте начнем с самого простого подхода из этих трех:

Process process = Runtime.getRuntime().exec("ping www.stackabuse.com");

Запуск этого кода приведет к выполнению команды, которую мы предоставили в строковом формате. Однако мы ничего не видим, когда запускаем это.

Чтобы проверить, правильно ли это выполнено, мы захотим получить доступ к объекту process . Давайте воспользуемся BufferedReader , чтобы взглянуть на то, что происходит:

public static void printResults(Process process) throws IOException {
    BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
    String line = "";
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
}

Теперь, когда мы запускаем этот метод после метода exec () , он должен дать что-то вроде:

Pinging www.stackabuse.com [104.18.57.23] with 32 bytes of data:
Reply from 104.18.57.23: bytes=32 time=21ms TTL=56
Reply from 104.18.57.23: bytes=32 time=21ms TTL=56
Reply from 104.18.57.23: bytes=32 time=21ms TTL=56
Reply from 104.18.57.23: bytes=32 time=21ms TTL=56

Ping statistics for 104.18.57.23:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 21ms, Maximum = 21ms, Average = 21ms

Имейте в виду, что нам придется извлекать информацию о процессе из экземпляров Process , когда мы будем рассматривать другие примеры.

Укажите рабочий каталог

Если вы хотите запустить команду, скажем, из определенной папки, мы бы сделали что-то вроде:

Process process = Runtime.getRuntime()
        .exec("cmd /c dir", null, new File("C:\\Users\\"));
      //.exec("sh -c ls", null, new File("Pathname")); for non-Windows users
printResults(process);

Здесь мы предоставили методу exec() команду , null для новых переменных среды и новый файл () , который установлен в качестве вашего рабочего каталога.

Стоит отметить добавление cmd/c перед такой командой, как dir .

Поскольку я работаю в Windows, это открывает cmd и /c выполняет последующую команду. В данном случае это реж. .

Причина, по которой это не было обязательным для примера ping , но является обязательным для этого примера, хорошо ответил пользователь SO.

Запуск предыдущего фрагмента кода приведет к:

Volume in drive C has no label.
 Volume Serial Number is XXXX-XXXX

 Directory of C:\Users

08/29/2019  05:01 PM              .
08/29/2019  05:01 PM              ..
08/18/2016  09:11 PM              Default.migrated
08/29/2019  05:01 PM              Public
05/15/2020  11:08 AM              User
               0 File(s)              0 bytes
               5 Dir(s)  212,555,214,848 bytes free

Давайте посмотрим, как мы могли бы предоставить предыдущую команду в нескольких отдельных частях, а не в одной строке:

Process process = Runtime.getRuntime().exec(
        new String[]{"cmd", "/c", "dir"},
        null, 
        new File("C:\\Users\\"));
        
printResults(process);

Запуск этого фрагмента кода также приведет к:

Volume in drive C has no label.
 Volume Serial Number is XXXX-XXXX

 Directory of C:\Users

08/29/2019  05:01 PM              .
08/29/2019  05:01 PM              ..
08/18/2016  09:11 PM              Default.migrated
08/29/2019  05:01 PM              Public
05/15/2020  11:08 AM              User
               0 File(s)              0 bytes
               5 Dir(s)  212,542,808,064 bytes free

В конечном счете, независимо от подхода – с использованием одной строки или массива строк, вводимая вами команда всегда будет разбита на массив перед обработкой базовой логикой.

Какой из них вы хотели бы использовать, сводится только к тому, какой из них вы считаете более читабельным.

Использование Переменных Среды

Давайте посмотрим, как мы можем использовать переменные среды:

Process process = Runtime.getRuntime().exec(
        "cmd /c echo %var1%",
        new String[]{"var1=value1"});
        
printResults(process);

Мы можем предоставить столько переменных среды, сколько захотим, в строковом массиве. Здесь мы только что напечатали значение var1 , используя echo .

Выполнение этого кода вернет:

value1

Запуск файлов .bat и .sh

Иногда просто гораздо проще выгрузить все в файл и запустить этот файл вместо того, чтобы добавлять все программно.

В зависимости от вашей операционной системы вы будете использовать файлы .bat или|/. sh|/. Давайте создадим его с содержимым:

Git Essentials

Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!

echo Hello World

Тогда давайте воспользуемся тем же подходом, что и раньше:

Process process = Runtime.getRuntime().exec(
        "cmd /c start file.bat",
        null,
        new File("C:\\Users\\User\\Desktop\\"));

Это откроет командную строку и запустит файл .bat в заданном нами рабочем каталоге.

Запуск этого кода, несомненно, приведет к:

Теперь, когда все перегруженные exec() подписи устранены, давайте рассмотрим класс ProcessBuilder и то, как мы можем выполнять команды с его помощью.

Конструктор процессов

ProcessBuilder является базовым механизмом, который выполняет команды при использовании метода Runtime.getRuntime().exec() :

/**
 * Executes the specified command and arguments in a separate process with
 * the specified environment and working directory.
 *...
*/
public Process exec(String[] cmdarray, String[] envp, File dir) throws IOException {
    return new ProcessBuilder(cmdarray)
        .environment(envp)
        .directory(dir)
        .start();
}

JavaDocs для Время выполнения класс

Взглянув на то, как ProcessBuilder принимает наши входные данные из метода exec() и запускает команду, мы также получаем хорошее представление о том, как ее использовать.

Он принимает строку [] cmdarray , и этого достаточно, чтобы запустить его. В качестве альтернативы мы можем предоставить ему дополнительные аргументы, такие как String[] envp и File dir .

Давайте рассмотрим эти варианты.

ProcessBuilder: Выполнение команды из строк

Вместо того, чтобы предоставлять одну строку , такую как cmd/c dir , в этом случае нам придется разбить ее. Например, если бы мы хотели перечислить файлы в C:/Users каталог, как и раньше, мы бы сделали:

ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.command("cmd", "/c", "dir C:\\Users");

Process process = processBuilder.start();
printResults(process);

Чтобы фактически выполнить Процесс , мы запускаем команду start() и присваиваем возвращаемое значение экземпляру Процесса .

Запуск этого кода приведет к:

 Volume in drive C has no label.
 Volume Serial Number is XXXX-XXXX

 Directory of C:\Users

08/29/2019  05:01 PM              .
08/29/2019  05:01 PM              ..
08/18/2016  09:11 PM              Default.migrated
08/29/2019  05:01 PM              Public
05/15/2020  11:08 AM              User
               0 File(s)              0 bytes
               5 Dir(s)  212,517,294,080 bytes free

Однако этот подход ничем не лучше предыдущего. Что полезно в классе ProcessBuilder , так это то, что он настраивается. Мы можем устанавливать вещи программно, а не только с помощью команд.

ProcessBuilder: Укажите рабочий каталог

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

processBuilder.command("cmd", "/c", "dir").directory(new File("C:\\Users\\"));

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

ProcessBuilder: Переменные среды

Используя методы ProcessBuilder , легко получить список переменных среды в виде Карты . Также легко установить переменные среды, чтобы ваша программа могла их использовать.

Давайте получим переменные среды, доступные в настоящее время, а затем добавим некоторые для последующего использования:

ProcessBuilder processBuilder = new ProcessBuilder();

Map environmentVariables  = processBuilder.environment();
environmentVariables.forEach((key, value) -> System.out.println(key + value));

Здесь мы упаковали возвращенные переменные среды в Map и запустили forEach() на нем, чтобы распечатать значения на нашей консоли.

Выполнение этого кода приведет к получению списка переменных среды, имеющихся на вашем компьютере:

DriverDataC:\Windows\System32\Drivers\DriverData
HerokuPathE:\Heroku
ProgramDataC:\ProgramData
...

Теперь давайте добавим переменную среды в этот список и будем использовать ее:

environmentVariables.put("var1", "value1");

processBuilder.command("cmd", "/c", "echo", "%var1%");
Process process = processBuilder.start();
printResults(process);

Запуск этого кода приведет к:

value1

Конечно, как только программа завершит работу, эта переменная не останется в списке.

ProcessBuilder: Запуск файлов .bat и .sh

Если вы хотите снова запустить файл, мы просто предоставим экземпляру ProcessBuilder необходимую информацию:

processBuilder
        .command("cmd", "/c", "start", "file.bat")
        .directory(new File("C:\\Users\\User\\Desktop"));
Process process = processBuilder.start();

Выполнение этого кода приводит к открытию командной строки и выполнению файла .bat .:

Вывод

В этой статье мы рассмотрели примеры выполнения команд оболочки на Java. Для этого мы использовали классы Runtime и ProcessBuilder .

Используя Java, мы можем запускать одну или несколько команд оболочки, выполнять сценарии оболочки, запускать терминал/командную строку, устанавливать рабочие каталоги и управлять переменными среды с помощью основных классов.