1. Was sind Prozesse im Betriebssystem und warum sie aus Java starten
Prozesse: von der JVM zu Bash und zurück
Wenn Sie den Computer einschalten und einen Browser, Messenger oder ein Spiel öffnen – all das sind separate Prozesse. Das Betriebssystem startet für jedes Programm seine eigene „Mini‑Welt“: Es weist Speicher und CPU‑Zeit zu und erlaubt den Zugriff auf Dateien und Netzwerk. So laufen Programme nebeneinander, ohne sich gegenseitig zu stören.
Ein Java‑Programm ist keine Ausnahme. Wenn Sie java MyApp starten, erstellt das System dafür einen eigenen Prozess mit allen benötigten Ressourcen. Darin läuft Ihr Programm: Es rechnet, zeichnet, liest Dateien – alles, was es soll.
Manchmal reicht Java allein jedoch nicht aus. Es kann nötig sein, ein anderes Programm um Hilfe zu bitten – einen Packer zu starten, um Dateien zu verpacken, ffmpeg aufzurufen, um Videos zu verarbeiten, oder einfach herauszufinden, welche Java‑Version auf dem Rechner installiert ist. Das ist das Starten eines externen Prozesses: Java sagt dem System „ruf mir dieses Tool auf“ und erhält anschließend das Ergebnis.
Im Kern ist das ein Weg, ein Programm flexibler zu machen: verschiedene Werkzeuge zu kombinieren, Routineaufgaben zu automatisieren oder Ihre Logik in bereits existierende Systemprozesse einzubetten. Manchmal ist es einfacher, einen externen Befehl einen Teil der Arbeit erledigen zu lassen, statt das Rad im Code neu zu erfinden.
JVM vs. externer Prozess
JVM‑Prozess – Ihr Programm, das in der Java Virtual Machine ausgeführt wird.
Externer Prozess – jedes andere Programm: Taschenrechner, Python‑Skript, Kommandozeile, sogar eine andere Java‑Instanz.
2. Die Klasse ProcessBuilder
In den „alten“ Zeiten startete Java Prozesse über die Methode Runtime.getRuntime().exec(). Das war weder besonders bequem noch sicher – etwa so, als wollte man einen Nagel mit einem Mikroskop einschlagen. Seit Java 5 gibt es daher die Klasse ProcessBuilder, mit der sich externe Prozesse komfortabler und flexibler erstellen, konfigurieren und starten lassen.
ProcessBuilder ist so etwas wie ein „Baukasten“, mit dem Sie alle Parameter des zukünftigen Prozesses im Voraus festlegen können: Befehl, Argumente, Arbeitsordner, Umgebungsvariablen usw.
Syntax: Prozess erstellen
ProcessBuilder pb = new ProcessBuilder("Befehl", "Argument1", "Argument2", ...);
- Das erste Argument – der Befehlsname (z. B. "ls", "dir", "ping", "java").
- Die übrigen – die Parameter für den Befehl.
Beispiel: den Befehl ls (Linux/Mac) oder dir (Windows) starten
ProcessBuilder pb;
if (System.getProperty("os.name").toLowerCase().contains("win")) {
pb = new ProcessBuilder("cmd.exe", "/c", "dir");
} else {
pb = new ProcessBuilder("ls", "-l");
}
Übrigens sind unter Windows Befehle wie dir, copy usw. keine separaten ausführbaren Dateien, sondern in die Kommandozeile (cmd.exe) „eingebaute“ Befehle. Deshalb müssen sie über cmd.exe /c ... gestartet werden.
Beispiel: einen einfachen Prozess starten
ProcessBuilder pb = new ProcessBuilder("echo", "Hello, Java!");
3. Die Umgebung des Prozesses konfigurieren
Argumente übergeben. Die Argumente des Befehls werden als separate Strings übergeben:
ProcessBuilder pb = new ProcessBuilder("ping", "google.com");
Arbeitsverzeichnis angeben. Standardmäßig wird der Prozess im gleichen Ordner gestartet wie Ihr Programm. Sie können aber auch ein anderes Verzeichnis explizit angeben:
pb.directory(new java.io.File("/tmp")); // Für Linux/Mac
pb.directory(new java.io.File("C:\\Temp")); // Für Windows
Umgebungsvariablen ändern. Jeder Prozess hat seinen eigenen Satz an Umgebungsvariablen (environment variables). Diese kann man hinzufügen oder ändern:
pb.environment().put("MY_VAR", "HelloFromJava");
Das kann nützlich sein, wenn der externe Prozess bestimmte Variablen erwartet.
4. Prozess starten
Methode start(). Wenn alles konfiguriert ist, kann der Prozess gestartet werden:
Process process = pb.start();
Die Methode start() liefert ein Objekt Process, mit dem Sie das gestartete Programm steuern können: seine Ausgabe lesen, in seine Eingabe schreiben, es beenden usw.
Ausnahmebehandlung. start() kann eine IOException auslösen, wenn der Befehl nicht gefunden wird, Berechtigungen fehlen oder ein anderer Startfehler auftritt.
Beispiel:
try {
Process process = pb.start();
// Mit dem Prozess arbeiten...
} catch (IOException e) {
System.out.println("Fehler beim Starten des Prozesses: " + e.getMessage());
}
5. Praxis: Einfache Befehle starten
Beispiel 1: Dateiliste in einem Ordner ausgeben
import java.io.*;
public class ProcessDemo {
public static void main(String[] args) {
// Befehl abhängig vom Betriebssystem wählen
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();
// Ausgabe des Prozesses lesen (stdout)
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream())
);
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// Auf das Ende des Prozesses warten
int exitCode = process.waitFor();
System.out.println("Prozess wurde mit Code beendet: " + exitCode);
} catch (IOException | InterruptedException e) {
System.out.println("Fehler: " + e.getMessage());
}
}
}
Was passiert hier?
- Wir ermitteln das Betriebssystem, um den richtigen Befehl zu wählen.
- Wir erstellen einen ProcessBuilder mit dem benötigten Befehl.
- Wir starten den Prozess über start().
- Wir lesen Zeilen aus dem stdout des Prozesses und geben sie auf dem Bildschirm aus.
- Wir warten auf das Ende des Prozesses (waitFor()).
- Wir geben den Rückgabecode aus (0 – Erfolg, anderes – Fehler).
Beispiel 2: Start von java -version
ProcessBuilder pb = new ProcessBuilder("java", "-version");
try {
Process process = pb.start();
// java -version schreibt nach stderr, deshalb lesen wir 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();
}
Wichtiger Punkt: Manche Befehle (z. B. java -version) schreiben nicht in die Standardausgabe (stdout), sondern in den Fehlerstrom (stderr). Daher muss man manchmal process.getErrorStream() lesen.
6. Plattformübergreifend: Unterschiede zwischen Windows und Linux/Mac
- Befehle und ihre Parameter können sich unterscheiden.
- Dateipfade werden unterschiedlich geschrieben (C:\Temp vs. /tmp).
- Einige Befehle (z. B. ls, cat) gibt es nur in Unix‑ähnlichen Systemen, unter Windows gibt es Entsprechungen (dir, type).
- Unter Windows werden eingebaute Befehle nur über cmd.exe /c Befehl gestartet.
Beispiel: OS prüfen:
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
}
Tipp: Testen Sie Ihre Programme immer auf den Zielbetriebssystemen, wenn Sie auf Plattformunabhängigkeit setzen.
7. Tabelle: Zentrale Methoden und Möglichkeiten von ProcessBuilder
| Methode/Feld | Zweck | Beispiel |
|---|---|---|
|
Prozess mit Befehl und Argumenten erstellen | |
|
Arbeitsverzeichnis festlegen | |
|
Umgebungsvariablen abrufen/ändern | |
|
Prozess starten | |
|
stdout des Prozesses abrufen | |
|
stderr des Prozesses abrufen | |
|
stdin des Prozesses abrufen | |
|
Auf Prozessende warten | |
|
Rückgabecode des Prozesses abrufen | |
8. Typische Fehler beim Starten externer Prozesse
Fehler Nr. 1: Befehl nicht gefunden. Wenn Sie sich im Befehlsnamen vertun oder er im System fehlt, erhalten Sie eine IOException: Cannot run program .... Zum Beispiel der Versuch, ls unter Windows zu starten.
Fehler Nr. 2: Falsche Übergabe von Argumenten. Packen Sie nicht den gesamten Befehl in einen einzigen String! Richtig: new ProcessBuilder("ping", "google.com"). Falsch: new ProcessBuilder("ping google.com").
Fehler Nr. 3: Unterschiede der Betriebssysteme nicht berücksichtigt. Ein Befehl, der unter Linux hervorragend funktioniert, kann unter Windows nicht existieren – und umgekehrt. Prüfen Sie immer das OS und passen Sie den Befehl an.
Fehler Nr. 4: Prozessausgabe nicht verarbeitet. Wenn Sie die Ausgabe eines Prozesses nicht lesen, kann er wegen eines vollen Puffers „hängen“ bleiben. Selbst wenn Sie die Ausgabe nicht verwenden wollen – lesen Sie sie und ignorieren Sie sie ggf.
Fehler Nr. 5: Streams nicht geschlossen. Schließen Sie die Streams des Prozesses nach der Verwendung, um Ressourcenlecks zu vermeiden.
Fehler Nr. 6: Ausnahmen nicht behandelt. Das Starten externer Prozesse ist fehleranfällig. Verwenden Sie immer try-catch und informieren Sie den Benutzer über Fehler.
GO TO FULL VERSION