2017-05-17 3 views
1

Ich habe ein scheinbar triviales Problem: Ich möchte ein Terminal aus einem Java-Prozess starten und dem Terminal einen oder zwei Befehle geben. Ich habe einen einfachen Beispielcode, der perfekt auf Windows mit CMD funktioniert. Aber auf einem Linux- oder Mac OS-Rechner konnte ich nicht genau dasselbe Verhalten erreichen. Ich bin mir bewusst, dass der Befehl geändert werden muss, aber leider konnte ich keine Argumente an ein Terminal auf dem Mac übergeben.Start natives Terminal mit Befehlsargumenten (Java)

Hier ist der Arbeits Code für Fenster:

import java.lang.ProcessBuilder.Redirect; 

public class ExecTest { 
    public static void main(String[] args){ 
     String cmd = "cmd /c start cmd.exe /K \"echo hello && echo bye\""; 
     try { 
      Process p = Runtime.getRuntime().exec(cmd); 
      p.waitFor(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 

Auf Lubuntu ich in der Lage gewesen, einen Terminal mit diesem Befehl zu erstellen:

lxterminal -l -e 'echo hello && echo bye && read' 

Das funktioniert aber nur, wenn sie von einem Terminal genannt, aber nicht mit dem Java-Prozess.

.

TLDR: Was die Linux und Mac entspricht dieses Befehls:

cmd /c start cmd.exe /K \"echo hello && echo bye\" 
+0

Wollen Sie wirklich ein "Terminal"? Oder willst du nur Shell-Kommandos ausführen? – bmargulies

+0

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? –

+0

@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

Antwort

1

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).

+0

Nur um zu überprüfen, ob wir auf der gleichen Seite sind, ruft Ihr Code ein Terminal an und öffnet das Terminal, damit der Benutzer sehen kann, was vor sich geht und die Drucke zu diesem Terminal gehen? (Ich frage, weil Sie den Inputstream sammeln und ausdrucken) – Haeri

+0

Holly Jesus! Dein Code funktioniert! Und ich war dabei, den Drehbuchweg zu gehen, auf dem ich ausführbare Skripte und all diesen Unsinn erstellen musste. Eine kleine Ergänzung, die ich gerne hätte: Wie kann ich das fürchterliche Mac OS-Prozessterminal deaktivieren? (Dinge wie "letzte Anmeldung: .." und die "Abmeldung", "Sitzung speichern", usw.). Auch was würde ich hinzufügen müssen, um eine "drücken Sie eine beliebige Taste zum Beenden" auf den Linux-Befehl? Vielen Dank für die Hilfe bis jetzt: D – Haeri

+0

Tut mir leid, ich weiß nicht, ob Sie diese Ausgabe programmgesteuert steuern können. Vielleicht gibt es Optionen innerhalb der Terminal-App. Bei der ersten Frage ist die Ausgabe, die protokolliert wird, die Ausgabe vom Terminal selbst, nur für den Fall (macOS Terminal druckt eine Zeile, wenn sie auf diese Weise aufgerufen wird). –

0

String[] cmd = {"echo", "hello", "&&", "echo", "hi"}; Runtime.getRuntime().exec(cmd);

Es gibt mehrere Möglichkeiten, dies zu tun, aber das sollte für Sie arbeiten. Ausführbare Dateien auf dem Mac sollten automatisch im Terminal laufen.

Möglicherweise ähnlich wie: How To Run Mac OS Terminal Commands From Java (Using Runtime?) Wenn überhaupt, haben sie ein paar Methoden zum Ausführen von Skripts im Terminal.

+0

Leider funktioniert das nicht. Auch der verlinkte Thread ist über das Ausführen von benutzerdefinierten Programmen, wo ich versuche, nur das Terminal zu starten und es einige Argumente übergeben. – Haeri