CodeGym /Kursy /JAVA 25 SELF /ProcessBuilder — uruchamianie procesów zewnętrznych

ProcessBuilder — uruchamianie procesów zewnętrznych

JAVA 25 SELF
Poziom 61 , Lekcja 0
Dostępny

1. Czym są procesy w systemie operacyjnym i po co uruchamiać je z Javy

Procesy: od JVM do bash i z powrotem

Kiedy włączasz komputer i uruchamiasz przeglądarkę, komunikator lub grę — to wszystko są oddzielne procesy. System operacyjny uruchamia dla każdego programu własny „mini-świat”: przydziela pamięć, czas procesora, zezwala na pracę z plikami i siecią. Dzięki temu programy działają obok siebie i sobie nie przeszkadzają.

Program w Javie nie jest wyjątkiem. Gdy uruchamiasz java MyApp, system tworzy dla niego osobny proces ze wszystkimi potrzebnymi zasobami. W jego wnętrzu działa już twój program: liczy, rysuje, czyta pliki — robi wszystko, co do niego należy.

Ale czasem sama Java nie wystarcza. Bywa, że trzeba poprosić o pomoc inny program — uruchomić archiwizer, aby spakować pliki, wywołać ffmpeg, aby przetworzyć wideo, albo po prostu sprawdzić, jaka wersja Javy jest zainstalowana na komputerze. To właśnie uruchamianie procesu zewnętrznego: Java mówi systemowi „uruchom mi to narzędzie”, a następnie odbiera wynik jego działania.

W istocie to sposób, by uczynić program bardziej elastycznym: połączyć różne narzędzia, zautomatyzować rutynę albo wpleść własną logikę w istniejące procesy systemowe. Niekiedy łatwiej zlecić część pracy zewnętrznemu poleceniu, niż wyważać otwarte drzwi w kodzie.

JVM vs. proces zewnętrzny

Proces JVM — twój program wykonywany w maszynie wirtualnej Javy.

Proces zewnętrzny — dowolny inny program: kalkulator, skrypt Pythona, wiersz poleceń, a nawet inna instancja Javy.

2. Klasa ProcessBuilder

W „zamierzchłych” czasach Java uruchamiała procesy przez metodę Runtime.getRuntime().exec(). Nie był to najwygodniejszy ani najbezpieczniejszy sposób — jak próba wbijania gwoździa mikroskopem. Dlatego od Javy 5 pojawiła się klasa ProcessBuilder, która pozwala tworzyć, konfigurować i uruchamiać procesy zewnętrzne w bardziej elastyczny i zrozumiały sposób.

ProcessBuilder to swoisty „konstruktor”, który pozwala z góry ustawić wszystkie parametry przyszłego procesu: polecenie, argumenty, katalog roboczy, zmienne środowiskowe itd.

Składnia: tworzenie procesu

ProcessBuilder pb = new ProcessBuilder("polecenie", "argument1", "argument2", ...);
  • Pierwszy argument — nazwa polecenia (np. "ls", "dir", "ping", "java").
  • Pozostałe — parametry polecenia.

Przykład: uruchomienie polecenia ls (Linux/Mac) lub 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");
}

Swoją drogą, w systemie Windows polecenia takie jak dir, copy itp. — to nie osobne pliki wykonywalne, lecz komendy „wbudowane” w wiersz poleceń (cmd.exe). Dlatego należy uruchamiać je przez cmd.exe /c ....

Przykład: uruchomienie prostego procesu

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

3. Konfigurowanie środowiska procesu

Przekazywanie argumentów. Argumenty polecenia przekazuje się jako osobne łańcuchy:

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

Określenie katalogu roboczego. Domyślnie proces startuje w tym samym katalogu co twój program. Można jednak jawnie wskazać inny katalog:

pb.directory(new java.io.File("/tmp"));      // Dla Linux/Mac
pb.directory(new java.io.File("C:\\Temp"));  // Dla Windows

Zmiana zmiennych środowiskowych. Każdy proces ma własny zestaw zmiennych środowiskowych (environment variables). Można je dodać lub zmodyfikować:

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

To bywa przydatne, jeśli proces zewnętrzny oczekuje określonych zmiennych.

4. Uruchamianie procesu

Metoda start(). Gdy wszystko skonfigurujesz, czas uruchomić proces:

Process process = pb.start();

Metoda start() zwraca obiekt Process, który pozwala zarządzać uruchomionym programem: czytać jego wyjście, pisać na jego wejście, zakańczać go itd.

Obsługa wyjątków. start() może zgłosić IOException, jeśli nie znaleziono polecenia, brakuje uprawnień albo wystąpił inny błąd uruchomienia.

Przykład:

try {
    Process process = pb.start();
    // Pracujemy z procesem...
} catch (IOException e) {
    System.out.println("Błąd uruchamiania procesu: " + e.getMessage());
}

5. Praktyka: uruchamianie prostych poleceń

Przykład 1: wypisanie listy plików w katalogu

import java.io.*;

public class ProcessDemo {
    public static void main(String[] args) {
        // Ustalamy polecenie w zależności od systemu operacyjnego
        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();

            // Czytamy wyjście procesu (stdout)
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream())
            );
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            // Czekamy na zakończenie procesu
            int exitCode = process.waitFor();
            System.out.println("Proces zakończył się kodem: " + exitCode);

        } catch (IOException | InterruptedException e) {
            System.out.println("Błąd: " + e.getMessage());
        }
    }
}

Co tu się dzieje?

  • Ustalamy, jaki to system operacyjny — aby wybrać właściwe polecenie.
  • Tworzymy ProcessBuilder z odpowiednim poleceniem.
  • Uruchamiamy proces przez start().
  • Czytamy linie ze stdout procesu i wypisujemy je na ekran.
  • Czekamy na zakończenie procesu (waitFor()).
  • Wypisujemy kod zakończenia (0 — sukces, inna wartość — błąd).

Przykład 2: uruchomienie java -version

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

    // java -version pisze na stderr, więc czytamy 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();
}

Ważna uwaga: Niektóre polecenia (np. java -version) wypisują informacje nie na standardowe wyjście (stdout), lecz do strumienia błędów (stderr). Dlatego czasem trzeba czytać process.getErrorStream().

6. Wieloplatformowość: różnice między Windows a Linux/Mac

  • Polecenia i ich parametry mogą się różnić.
  • Ścieżki do plików zapisuje się inaczej (C:\Temp vs /tmp).
  • Niektóre polecenia (np. ls, cat) istnieją tylko w systemach uniksowych, a w Windows zastępują je odpowiedniki (dir, type).
  • W Windows polecenia wbudowane uruchamia się tylko przez cmd.exe /c polecenie.

Przykład sprawdzania systemu operacyjnego:

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
}

Wskazówka: Zawsze testuj swoje programy w docelowych systemach operacyjnych, jeśli liczysz na wieloplatformowość.

7. Tabela: najważniejsze metody i możliwości ProcessBuilder

Metoda/pole Przeznaczenie Przykład użycia
new ProcessBuilder(String...)
Utworzyć proces z poleceniem i argumentami
new ProcessBuilder("ls", "-l")
.directory(File)
Ustawić katalog roboczy
.directory(new File("/tmp"))
.environment()
Pobrać/zmienić zmienne środowiskowe
.environment().put("VAR", "value")
.start()
Uruchomić proces
Process p = pb.start()
Process.getInputStream()
Uzyskać stdout procesu
InputStream
Process.getErrorStream()
Uzyskać stderr procesu
InputStream
Process.getOutputStream()
Uzyskać stdin procesu
OutputStream
Process.waitFor()
Czekać na zakończenie procesu
int code = p.waitFor()
Process.exitValue()
Pobrać kod zakończenia procesu
int code = p.exitValue()

8. Typowe błędy przy uruchamianiu procesów zewnętrznych

Błąd nr 1: Nie znaleziono polecenia. Jeśli pomylisz się w nazwie polecenia lub go brakuje w systemie, otrzymasz IOException: Cannot run program .... Na przykład próba uruchomienia ls w Windows.

Błąd nr 2: Nieprawidłowe przekazywanie argumentów. Nie łącz całego polecenia w jeden łańcuch! Poprawnie: new ProcessBuilder("ping", "google.com"). Niepoprawnie: new ProcessBuilder("ping google.com").

Błąd nr 3: Zignorowano różnice systemów. Polecenie, które świetnie działa w Linuksie, może nie istnieć w Windows i odwrotnie. Zawsze sprawdzaj system i dostosuj polecenie.

Błąd nr 4: Nieobsłużone wyjście procesu. Jeśli nie będziesz czytać wyjścia procesu, może on „zawisnąć” z powodu przepełnienia bufora. Nawet jeśli nie zamierzasz używać wyjścia — czytaj je i, na przykład, po prostu ignoruj.

Błąd nr 5: Nie zamknięto strumieni. Strumienie procesu należy zamykać po użyciu, aby uniknąć wycieków zasobów.

Błąd nr 6: Nieobsłużone wyjątki. Uruchamianie procesu zewnętrznego jest obarczone ryzykiem. Zawsze używaj try-catch i informuj użytkownika o błędach.

1
Zadanie
JAVA 25 SELF, poziom 61, lekcja 0
Niedostępne
Inspektor systemu 🖥️
Inspektor systemu 🖥️
1
Zadanie
JAVA 25 SELF, poziom 61, lekcja 0
Niedostępne
Cyfrowy Tropiciel 📁
Cyfrowy Tropiciel 📁
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION