Ich schlage vor, Sie ProcessBuilder
verwenden aus leichter Ausgabeumleitung und die Fähigkeit zu profitieren, es zu konsumieren, ohne Threads, und übergeben Sie auch den Befehl als String[]
statt flach String
der Lage sein, die verschiedenen Wickel Ansätze zu unterstützen. Wenn Sie lieber mit Runtime.exec()
bleiben, unterstützt es auch String[]
, aber das folgende Beispiel verwendet ProcessBuilder
.
static int executeInTerminal(String command) throws IOException, InterruptedException {
final String[] wrappedCommand;
if (isWindows) {
wrappedCommand = new String[]{ "cmd", "/c", "start", "/wait", "cmd.exe", "/K", command };
}
else if (isLinux) {
wrappedCommand = new String[]{ "xterm", "-e", "bash", "-c", command};
}
else if (isMac) {
wrappedCommand = new String[]{"osascript",
"-e", "tell application \"Terminal\" to activate",
"-e", "tell application \"Terminal\" to do script \"" + command + ";exit\""};
}
else {
throw new RuntimeException("Unsupported OS ☹");
}
Process process = new ProcessBuilder(wrappedCommand)
.redirectErrorStream(true)
.start();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line); // Your superior logging approach here
}
}
return process.waitFor();
}
Getestet auf 3 Betriebssystemen. Die 3 booleans is{Windows|Linux|Mac}
sind hier nicht erklärt, da OS-Erkennung ein anderes Thema ist, so dass ich es für das Beispiel einfach gehalten und aus der Methode heraus behandelt habe.
Die ProcessBuilder
ist so konfiguriert, stderr auf stdout umzuleiten, so dass ein einzelner Stream gelesen werden muss. Dieser Stream wird dann gelesen und protokolliert, weil Sie stderr und stdout konsumieren müssen, falls das Terminal selbst Dinge druckt (nicht den Befehl, den Sie im Terminal ausführen, handelt es sich um das, was das Terminal selbst druckt), wenn es zu viel druckt riskiere, dass der Prozess unbegrenzt blockiert wird, und warte darauf, dass der Puffer gelesen wird. See this nice answer.
Für macOS, wenn der Befehl, den Sie übergeben, immer ein einzelnes ausführbares Skript ist, könnten Sie auch {"open", "-a", "Terminal", command}
verwenden, aber das funktioniert nicht mit echo hello && echo bye
. Ähnlich könnten Sie {"/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal", command}
verwenden, wodurch Sie eine zweite Instanz der App erhalten, aber mit denselben Einschränkungen.
Schlussbemerkung: Sie können eine sinnvolle grundlegende Implementierung anbieten, aber Sie müssen wahrscheinlich konfigurieren, um alternative Terminal-Anwendungen zu ermöglichen (besonders unter Linux, wo es sehr variiert).
Wollen Sie wirklich ein "Terminal"? Oder willst du nur Shell-Kommandos ausführen? – bmargulies
Ich kann ein Terminal mit dem Befehl 'gnome-terminal' und dem gleichen Java-Code auf meinem Ubuntu-Rechner öffnen. Könnten Sie das bitte versuchen? –
@bmargulies Ja. Um etwas Kontext zu geben: Ich habe einen Code-Editor gemacht, der etwas Code kompilieren kann. Ich habe auch mein eigenes Terminal erstellt, das das Ergebnis des Exec-Prozesses anzeigt. Aber es gibt einige Randfälle, in denen ich die In- und Outputstreams nicht vollständig anzeigen kann. Als Backup möchte ich dem Benutzer erlauben, den Code in einem nativen Terminal auszuführen. Also ja, ich möchte ein neues natives Terminal erstellen und Code ausführen. – Haeri