JavaRush /Курси /JAVA 25 SELF /ProcessBuilder — запуск зовнішніх процесів

ProcessBuilder — запуск зовнішніх процесів

JAVA 25 SELF
Рівень 61 , Лекція 0
Відкрита

1. Що таке процеси в ОС і навіщо їх запускати з Java

Процеси: від JVM до bash і назад

Коли ви вмикаєте комп’ютер і відкриваєте браузер, месенджер або гру — усе це окремі процеси. Операційна система запускає для кожної програми власний «мінісвіт»: виділяє пам’ять, процесорний час, дозволяє працювати з файлами та мережею. Тож програми живуть поруч, але не заважають одна одній.

Java‑програма — не виняток. Коли ви запускаєте java MyApp, система створює для неї окремий процес із усіма необхідними ресурсами. Усередині нього вже працює ваша програма: рахує, малює, читає файли — усе, що їй потрібно.

Але інколи однієї Java замало. Буває, потрібно попросити про допомогу іншу програму — запустити архіватор, щоб заархівувати файли, викликати ffmpeg, щоб обробити відео, або просто дізнатися, яка версія Java встановлена на комп’ютері. Це і є запуск зовнішнього процесу: Java каже системі «виконай ось цю утиліту», а потім отримує результат її роботи.

По суті це спосіб зробити програму гнучкішою: об’єднати різні інструменти, автоматизувати рутину або вбудувати власну логіку в уже наявні системні процеси. Іноді простіше попросити зовнішню команду виконати частину роботи, ніж винаходити велосипед у коді.

JVM проти зовнішнього процесу

JVM‑процес — ваша програма, що виконується у віртуальній машині Java.

Зовнішній процес — будь-яка інша програма: калькулятор, Python‑скрипт, командний рядок, навіть інший екземпляр Java.

2. Клас ProcessBuilder

У «давні» часи Java запускала процеси через метод Runtime.getRuntime().exec(). Це був не найзручніший і не надто безпечний спосіб — як спроба забити цвях мікроскопом. Тому, починаючи з Java 5, з’явився клас ProcessBuilder, який дає змогу створювати, налаштовувати та запускати зовнішні процеси більш гнучко і зрозуміло.

ProcessBuilder — це своєрідний «конструктор», що дає змогу заздалегідь задати всі параметри майбутнього процесу: команду, аргументи, робочу директорію, змінні середовища тощо.

Синтаксис: створення процесу

ProcessBuilder pb = new ProcessBuilder("команда", "аргумент1", "аргумент2", ...);
  • Перший аргумент — назва команди (наприклад, "ls", "dir", "ping", "java").
  • Решта — параметри для команди.

Приклад: запуск команди ls (Linux/macOS) або dir (Windows)

ProcessBuilder pb;
if (System.getProperty("os.name").toLowerCase().contains("win")) {
    pb = new ProcessBuilder("cmd.exe", "/c", "dir");
} else {
    pb = new ProcessBuilder("ls", "-l");
}

До речі, у Windows команди на кшталт dir, copy тощо — це не окремі виконувані файли, а «вбудовані» в командний рядок (cmd.exe) команди. Тому їх потрібно запускати через cmd.exe /c ....

Приклад: запуск простого процесу

ProcessBuilder pb = new ProcessBuilder("echo", "Hello, Java!");

3. Налаштування середовища процесу

Передавання аргументів. Аргументи команди передаються окремими рядками:

ProcessBuilder pb = new ProcessBuilder("ping", "google.com");

Вказання робочої директорії. За замовчуванням процес запускається в тій самій папці, що й ваша програма. Але можна явно вказати іншу директорію:

pb.directory(new java.io.File("/tmp"));      // Для Linux/macOS
pb.directory(new java.io.File("C:\\Temp"));  // Для Windows

Зміна змінних середовища. Кожен процес має власний набір змінних середовища (environment variables). Їх можна додати або змінити:

pb.environment().put("MY_VAR", "HelloFromJava");

Це може бути корисно, якщо зовнішній процес очікує певні змінні.

4. Запуск процесу

Метод start(). Коли ви все налаштували, час запускати процес:

Process process = pb.start();

Метод start() повертає об’єкт Process, що дає змогу керувати запущеною програмою: читати її вивід, писати у її ввід, завершувати її тощо.

Обробка винятків. start() може згенерувати IOException, якщо команду не знайдено, бракує прав або виникла інша помилка запуску.

Приклад:

try {
    Process process = pb.start();
    // Працюємо з процесом...
} catch (IOException e) {
    System.out.println("Помилка запуску процесу: " + e.getMessage());
}

5. Практика: запуск простих команд

Приклад 1: вивести список файлів у теці

import java.io.*;

public class ProcessDemo {
    public static void main(String[] args) {
        // Визначаємо команду залежно від ОС
        ProcessBuilder pb;
        if (System.getProperty("os.name").toLowerCase().contains("win")) {
            pb = new ProcessBuilder("cmd.exe", "/c", "dir");
        } else {
            pb = new ProcessBuilder("ls", "-l");
        }

        try {
            Process process = pb.start();

            // Читаємо вивід процесу (stdout)
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream())
            );
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            // Чекаємо завершення процесу
            int exitCode = process.waitFor();
            System.out.println("Процес завершено з кодом: " + exitCode);

        } catch (IOException | InterruptedException e) {
            System.out.println("Помилка: " + e.getMessage());
        }
    }
}

Що тут відбувається?

  • Визначаємо, яка ОС — щоб обрати правильну команду.
  • Створюємо ProcessBuilder з потрібною командою.
  • Запускаємо процес через start().
  • Читаємо рядки зі stdout процесу та виводимо їх на екран.
  • Чекаємо завершення процесу (waitFor()).
  • Виводимо код повернення (0 — успіх, інше — помилка).

Приклад 2: запуск java -version

ProcessBuilder pb = new ProcessBuilder("java", "-version");
try {
    Process process = pb.start();

    // java -version виводить у stderr, тому читаємо getErrorStream()
    BufferedReader reader = new BufferedReader(
        new InputStreamReader(process.getErrorStream())
    );
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
    process.waitFor();
} catch (IOException | InterruptedException e) {
    e.printStackTrace();
}

Важливий нюанс: Деякі команди (наприклад, java -version) виводять інформацію не в стандартний вивід (stdout), а в потік помилок (stderr). Тому інколи потрібно читати process.getErrorStream().

6. Кросплатформність: відмінності між Windows і Linux/Mac

  • Команди та їх параметри можуть відрізнятися.
  • Шляхи до файлів записуються по‑різному (C:\Temp vs /tmp).
  • Деякі команди (наприклад, ls, cat) є лише в Unix‑подібних системах, а у Windows їх замінюють аналоги (dir, type).
  • У Windows вбудовані команди запускаються лише через cmd.exe /c команда.

Приклад перевірки ОС:

String os = System.getProperty("os.name").toLowerCase();
if (os.contains("win")) {
    // Windows
} else if (os.contains("mac")) {
    // macOS
} else if (os.contains("nix") || os.contains("nux")) {
    // Linux
}

Порада: Завжди тестуйте ваші програми на цільових ОС, якщо розраховуєте на кросплатформеність.

7. Таблиця: основні методи та можливості ProcessBuilder

Метод/поле Призначення Приклад використання
new ProcessBuilder(String...)
Створити процес з командою та аргументами
new ProcessBuilder("ls", "-l")
.directory(File)
Вказати робочу директорію
.directory(new File("/tmp"))
.environment()
Отримати/змінити змінні середовища
.environment().put("VAR", "value")
.start()
Запустити процес
Process p = pb.start()
Process.getInputStream()
Отримати stdout процесу
InputStream
Process.getErrorStream()
Отримати stderr процесу
InputStream
Process.getOutputStream()
Отримати stdin процесу
OutputStream
Process.waitFor()
Очікувати на завершення процесу
int code = p.waitFor()
Process.exitValue()
Отримати код повернення процесу
int code = p.exitValue()

8. Типові помилки під час запуску зовнішніх процесів

Помилка № 1: Команду не знайдено. Якщо ви помилилися в назві команди або її немає в системі, отримаєте IOException: Cannot run program .... Наприклад, спроба запустити ls у Windows.

Помилка № 2: Некоректна передача аргументів. Не потрібно об’єднувати всю команду в один рядок! Правильно: new ProcessBuilder("ping", "google.com"). Неправильно: new ProcessBuilder("ping google.com").

Помилка № 3: Не врахували відмінності ОС. Команда, що чудово працює на Linux, може не існувати в Windows, і навпаки. Завжди перевіряйте ОС і адаптуйте команду.

Помилка № 4: Не обробили вивід процесу. Якщо не читати вивід процесу, він може «зависнути» через переповнення буфера. Навіть якщо ви не збираєтеся використовувати вивід — читайте його й, наприклад, просто ігноруйте.

Помилка № 5: Не закрили потоки. Потоки процесу потрібно закривати після використання, щоб не було витоків ресурсів.

Помилка № 6: Не обробили винятки. Запуск зовнішнього процесу — ризикована справа. Завжди використовуйте try-catch та інформуйте користувача про помилки.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ