Выполнение команд оболочки в Java

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

Вступление

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

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

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

Runtime.exec ()

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

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

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

  • public Process exec(String command) - выполняет команду, содержащуюся в command в отдельном процессе.
  • public Process exec(String command, String[] envp) - выполняет command с массивом переменных среды. Они представлены в виде массива строк в формате name=value
  • public Process exec(String command, String[] envp, File dir) - выполняет command с указанными переменными среды из каталога dir
  • public Process exec(String cmdArray[]) - выполняет команду в виде массива строк.
  • public Process exec(String cmdArray[], String[] envp) - выполняет команду с указанными переменными среды.
  • public Process exec(String cmdarray[], String[] envp, File dir) - выполняет команду с указанными переменными среды из каталога dir

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

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

Вам решать, что вы хотите использовать - exec("dir /folder") или exec(new String[]{"dir", "/folder"}

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

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

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

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

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

Чтобы проверить, правильно ли это работает, мы хотим получить объект 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() с command , null для новых переменных среды и new File() который установлен в качестве нашего рабочего каталога.

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

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

Причина , почему это не было обязательным для 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 <DIR> . 
 08/29/2019 05:01 PM <DIR> .. 
 08/18/2016 09:11 PM <DIR> Default.migrated 
 08/29/2019 05:01 PM <DIR> Public 
 05/15/2020 11:08 AM <DIR> 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 <DIR> . 
 08/29/2019 05:01 PM <DIR> .. 
 08/18/2016 09:11 PM <DIR> Default.migrated 
 08/29/2019 05:01 PM <DIR> Public 
 05/15/2020 11:08 AM <DIR> User 
 0 File(s) 0 bytes 
 5 Dir(s) 212,542,808,064 bytes free 

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

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

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

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

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

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

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

 value1 

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

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

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

 echo Hello World 

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

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

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

Выполнение этого кода, безусловно, приведет к:

командная строка с приветмиром{.ezlazyload}

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

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 для класса Runtime]{.small}

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

Он принимает String[] 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); 

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

Запуск этого кода даст:

 Volume in drive C has no label. 
 Volume Serial Number is XXXX-XXXX 
 
 Directory of C:\Users 
 
 08/29/2019 05:01 PM <DIR> . 
 08/29/2019 05:01 PM <DIR> .. 
 08/18/2016 09:11 PM <DIR> Default.migrated 
 08/29/2019 05:01 PM <DIR> Public 
 05/15/2020 11:08 AM <DIR> 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 , легко получить список переменных среды в виде Map . Также легко установить переменные среды, чтобы ваша программа могла их использовать.

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

 ProcessBuilder processBuilder = new ProcessBuilder(); 
 
 Map<String, String> 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

командная строка привет мир batфайл{.ezlazyload}

Заключение

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

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

comments powered by Disqus