Ausführen von Shell-Befehlen in Java
On Oktober 23, 2021 by adminEinführung
In diesem Artikel sehen wir uns an, wie wir die Klassen Runtime
und ProcessBuilder
nutzen können, um Shell-Befehle und Skripte mit Java auszuführen.
Wir verwenden Computer, um viele Dinge in unserer täglichen Arbeit zu automatisieren. Systemadministratoren führen ständig viele Befehle aus, von denen einige sehr repetitiv sind und nur minimale Änderungen zwischen den einzelnen Durchläufen erfordern.
Auch dieser Prozess ist reif für die Automatisierung. Es ist nicht nötig, alles manuell auszuführen. Mit Java können wir einzelne oder mehrere Shell-Befehle ausführen, Shell-Skripte ausführen, die Terminal-/Befehlseingabeaufforderung starten, Arbeitsverzeichnisse festlegen und Umgebungsvariablen durch Kernklassen manipulieren.
Runtime.exec()
Die Klasse Runtime
in Java ist eine High-Level-Klasse, die in jeder einzelnen Java-Anwendung vorhanden ist. Über sie kommuniziert die Anwendung selbst mit der Umgebung, in der sie sich befindet.
Indem wir die mit unserer Anwendung verbundene Runtime über die getRuntime()
-Methode extrahieren, können wir die exec()
-Methode verwenden, um Befehle direkt auszuführen oder .bat
/.sh
-Dateien zu starten.
Die exec()
-Methode bietet einige überladene Varianten:
-
public Process exec(String command)
– Führt den incommand
enthaltenen Befehl in einem separaten Prozess aus. -
public Process exec(String command, String envp)
– Führtcommand
aus, mit einem Array von Umgebungsvariablen. Sie werden als Array von Strings im Formatname=value
bereitgestellt. -
public Process exec(String command, String envp, File dir)
– Führt den Befehlcommand
mit den angegebenen Umgebungsvariablen aus dem Verzeichnisdir
aus. -
public Process exec(String cmdArray)
– Führt einen Befehl in Form eines Arrays von Strings aus. -
public Process exec(String cmdArray, String envp)
– Führt einen Befehl mit den angegebenen Umgebungsvariablen aus. -
public Process exec(String cmdarray, String envp, File dir)
– Führt einen Befehl mit den angegebenen Umgebungsvariablen aus dem Verzeichnisdir
aus.
Es ist erwähnenswert, dass diese Prozesse außerhalb des Interpreters ausgeführt werden und systemabhängig sind.
Was auch erwähnenswert ist, ist der Unterschied zwischen String command
und String cmdArray
. Sie bewirken das Gleiche. Ein command
wird sowieso in ein Array zerlegt, also sollte die Verwendung einer dieser beiden Methoden die gleichen Ergebnisse liefern.
Es liegt an Ihnen zu entscheiden, ob Sie exec("dir /folder")
oder exec(new String{"dir", "/folder"}
verwenden möchten.
Lassen Sie uns ein paar Beispiele schreiben, um zu sehen, wie sich diese überladenen Methoden voneinander unterscheiden.
Ausführen eines Befehls aus einer Zeichenkette
Beginnen wir mit dem einfachsten Ansatz dieser drei Methoden:
Process process = Runtime.getRuntime().exec("ping www.stackabuse.com");
Das Ausführen dieses Codes führt den Befehl aus, den wir im String-Format angegeben haben. Wir sehen jedoch nichts, wenn wir ihn ausführen.
Um zu überprüfen, ob er korrekt ausgeführt wurde, müssen wir das process
-Objekt in die Finger bekommen. Verwenden wir ein BufferedReader
, um einen Blick darauf zu werfen, was vor sich geht:
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); }}
Wenn wir diese Methode nach der exec()
-Methode ausführen, sollte das Ergebnis in etwa so aussehen:
Pinging www.stackabuse.com with 32 bytes of data:Reply from 104.18.57.23: bytes=32 time=21ms TTL=56Reply from 104.18.57.23: bytes=32 time=21ms TTL=56Reply from 104.18.57.23: bytes=32 time=21ms TTL=56Reply from 104.18.57.23: bytes=32 time=21ms TTL=56Ping 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
Denken Sie daran, dass wir die Prozessinformationen aus den Process
-Instanzen extrahieren müssen, wenn wir andere Beispiele durchgehen.
Bestimmen des Arbeitsverzeichnisses
Wenn man einen Befehl z.B. von einem bestimmten Ordner aus ausführen möchte, würde man etwas in der Art machen:
Process process = Runtime.getRuntime() .exec("cmd /c dir", null, new File("C:\Users\")); //.exec("sh -c ls", null, new File("Pathname")); for non-Windows usersprintResults(process);
Hier haben wir die exec()
-Methode mit einem command
, einem null
für neue Umgebungsvariablen und einem new File()
versehen, das als unser Arbeitsverzeichnis festgelegt ist.
Besonders erwähnenswert ist die Hinzufügung von cmd /c
vor einem Befehl wie dir
.
Da ich unter Windows arbeite, öffnet dies die cmd
und /c
führt den nachfolgenden Befehl aus. In diesem Fall ist es dir
.
Der Grund, warum dies für das ping
-Beispiel nicht obligatorisch war, aber für dieses Beispiel obligatorisch ist, wurde von einem SO-Benutzer sehr schön beantwortet.
Das Ausführen des vorherigen Code-Stücks führt zu:
Volume in drive C has no label. Volume Serial Number is XXXX-XXXX Directory of C:\Users08/29/2019 05:01 PM <DIR> .08/29/2019 05:01 PM <DIR> ..08/18/2016 09:11 PM <DIR> Default.migrated08/29/2019 05:01 PM <DIR> Public05/15/2020 11:08 AM <DIR> User 0 File(s) 0 bytes 5 Dir(s) 212,555,214,848 bytes free
Schauen wir uns an, wie wir den vorherigen Befehl in mehreren einzelnen Teilen liefern könnten, anstatt in einem einzigen String:
Process process = Runtime.getRuntime().exec( new String{"cmd", "/c", "dir"}, null, new File("C:\Users\")); printResults(process);
Das Ausführen dieses Code-Stücks führt auch zu:
Volume in drive C has no label. Volume Serial Number is XXXX-XXXX Directory of C:\Users08/29/2019 05:01 PM <DIR> .08/29/2019 05:01 PM <DIR> ..08/18/2016 09:11 PM <DIR> Default.migrated08/29/2019 05:01 PM <DIR> Public05/15/2020 11:08 AM <DIR> User 0 File(s) 0 bytes 5 Dir(s) 212,542,808,064 bytes free
Endlich wird der eingegebene Befehl unabhängig von der Vorgehensweise – Verwendung eines einzelnen Strings oder eines String-Arrays – immer in ein Array zerlegt, bevor er von der zugrunde liegenden Logik verarbeitet wird.
Welche Methode Sie verwenden möchten, hängt davon ab, welche Sie besser lesen können.
Verwendung von Umgebungsvariablen
Lassen Sie uns einen Blick darauf werfen, wie wir Umgebungsvariablen verwenden können:
Process process = Runtime.getRuntime().exec( "cmd /c echo %var1%", new String{"var1=value1"}); printResults(process);
Wir können so viele Umgebungsvariablen innerhalb des String-Arrays angeben, wie wir möchten. Hier haben wir gerade den Wert von var1
mit echo
ausgegeben.
Die Ausführung dieses Codes ergibt:
value1
Ausführen von .bat- und .sh-Dateien
Manchmal ist es viel einfacher, alles in eine Datei auszulagern und diese Datei auszuführen, anstatt alles programmatisch hinzuzufügen.
Abhängig von Ihrem Betriebssystem verwenden Sie entweder .bat
– oder .sh
-Dateien. Erstellen wir eine mit folgendem Inhalt:
echo Hello World
Dann gehen wir genauso vor wie zuvor:
Process process = Runtime.getRuntime().exec( "cmd /c start file.bat", null, new File("C:\Users\User\Desktop\"));
Damit öffnen wir die Eingabeaufforderung und führen die .bat
-Datei in dem von uns festgelegten Arbeitsverzeichnis aus.
Das Ausführen dieses Codes ergibt mit Sicherheit:
Nachdem wir uns um alle überladenen exec()
-Signaturen gekümmert haben, wollen wir uns nun die ProcessBuilder
-Klasse ansehen und wie wir mit ihr Befehle ausführen können.
ProcessBuilder
ProcessBuilder
ist der zugrundeliegende Mechanismus, der die Befehle ausführt, wenn wir die Runtime.getRuntime().exec()
-Methode verwenden:
/** * 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 für die Runtime
-Klasse
Wenn wir uns ansehen, wie die ProcessBuilder
unsere Eingabe von der exec()
-Methode entgegennimmt und den Befehl ausführt, bekommen wir eine gute Vorstellung davon, wie wir sie auch verwenden können.
Es akzeptiert ein String cmdarray
, und das ist genug, um es zum Laufen zu bringen. Alternativ können wir ihn mit optionalen Argumenten wie String envp
und File dir
versorgen.
Lassen Sie uns diese Optionen untersuchen.
ProcessBuilder: Ausführen von Befehlen aus Zeichenketten
Anstatt eine einzelne Zeichenkette, wie cmd /c dir
, angeben zu können, müssen wir sie in diesem Fall aufschlüsseln. Wenn wir zum Beispiel die Dateien im Verzeichnis C:/Users
auflisten wollen, wie zuvor, würden wir Folgendes tun:
ProcessBuilder processBuilder = new ProcessBuilder();processBuilder.command("cmd", "/c", "dir C:\Users");Process process = processBuilder.start();printResults(process);
Um eine Process
tatsächlich auszuführen, führen wir den Befehl start()
aus und weisen den zurückgegebenen Wert einer Process
-Instanz zu.
Die Ausführung dieses Codes ergibt:
Volume in drive C has no label. Volume Serial Number is XXXX-XXXX Directory of C:\Users08/29/2019 05:01 PM <DIR> .08/29/2019 05:01 PM <DIR> ..08/18/2016 09:11 PM <DIR> Default.migrated08/29/2019 05:01 PM <DIR> Public05/15/2020 11:08 AM <DIR> User 0 File(s) 0 bytes 5 Dir(s) 212,517,294,080 bytes free
Dieser Ansatz ist jedoch nicht besser als der vorherige. Das Gute an der Klasse ProcessBuilder
ist, dass sie anpassbar ist. Wir können Dinge programmatisch einstellen, nicht nur über Befehle.
ProcessBuilder: Angabe des Arbeitsverzeichnisses
Anstatt das Arbeitsverzeichnis über den Befehl anzugeben, legen wir es programmatisch fest:
processBuilder.command("cmd", "/c", "dir").directory(new File("C:\Users\"));
Hier haben wir das Arbeitsverzeichnis auf das gleiche wie zuvor festgelegt, aber wir haben diese Definition aus dem Befehl selbst ausgelagert. Wenn Sie diesen Code ausführen, erhalten Sie das gleiche Ergebnis wie im letzten Beispiel.
ProcessBuilder: Umgebungsvariablen
Mit den Methoden von ProcessBuilder
ist es einfach, eine Liste von Umgebungsvariablen in Form eines Map
abzurufen. Es ist auch einfach, Umgebungsvariablen zu setzen, damit dein Programm sie verwenden kann.
Lassen Sie uns die derzeit verfügbaren Umgebungsvariablen abrufen und dann einige für die spätere Verwendung hinzufügen:
ProcessBuilder processBuilder = new ProcessBuilder();Map<String, String> environmentVariables = processBuilder.environment();environmentVariables.forEach((key, value) -> System.out.println(key + value));
Hier haben wir die zurückgegebenen Umgebungsvariablen in ein Map
gepackt und ein forEach()
darauf ausgeführt, um die Werte auf unserer Konsole auszugeben.
Wenn man diesen Code ausführt, erhält man eine Liste der Umgebungsvariablen, die auf dem Rechner vorhanden sind:
DriverDataC:\Windows\System32\Drivers\DriverDataHerokuPathE:\HerokuProgramDataC:\ProgramData...
Nun fügen wir eine Umgebungsvariable zu dieser Liste hinzu und verwenden sie:
environmentVariables.put("var1", "value1");processBuilder.command("cmd", "/c", "echo", "%var1%");Process process = processBuilder.start();printResults(process);
Wenn man diesen Code ausführt, erhält man:
value1
Natürlich bleibt diese Variable nicht in der Liste, wenn das Programm beendet ist.
ProcessBuilder: Ausführen von .bat- und .sh-Dateien
Wenn Sie eine Datei ausführen möchten, geben Sie der ProcessBuilder
-Instanz einfach die erforderlichen Informationen:
processBuilder .command("cmd", "/c", "start", "file.bat") .directory(new File("C:\Users\User\Desktop"));Process process = processBuilder.start();
Das Ausführen dieses Codes führt dazu, dass sich die Eingabeaufforderung öffnet und die .bat
-Datei ausgeführt wird:
Abschluss
In diesem Artikel haben wir Beispiele für das Ausführen von Shell-Befehlen in Java erkundet. Dazu haben wir die Klassen Runtime
und ProcessBuilder
verwendet.
Mit Java können wir einzelne oder mehrere Shell-Befehle ausführen, Shell-Skripte ausführen, die Terminal-/Befehlseingabeaufforderung starten, Arbeitsverzeichnisse festlegen und Umgebungsvariablen über Kernklassen manipulieren.
Schreibe einen Kommentar