Ich habe das folgende Codebeispiel unten. Wobei Sie einen Befehl in die Bash-Shell eingeben können, dh echo test
das Ergebnis zurückgeben lassen. Allerdings nach dem ersten Lesen. Andere Ausgabestreams funktionieren nicht?
Warum ist das so oder mache ich etwas falsch? Mein Endziel ist es, eine geplante Thread-Aufgabe zu erstellen, die regelmäßig einen Befehl für / bash ausführt, damit die OutputStream
und zusammenarbeiten InputStream
müssen und nicht aufhören zu arbeiten. Ich habe auch den Fehler java.io.IOException: Broken pipe
irgendwelche Ideen erfahren ?
Vielen Dank.
String line;
Scanner scan = new Scanner(System.in);
Process process = Runtime.getRuntime ().exec ("/bin/bash");
OutputStream stdin = process.getOutputStream ();
InputStream stderr = process.getErrorStream ();
InputStream stdout = process.getInputStream ();
BufferedReader reader = new BufferedReader (new InputStreamReader(stdout));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin));
String input = scan.nextLine();
input += "\n";
writer.write(input);
writer.flush();
input = scan.nextLine();
input += "\n";
writer.write(input);
writer.flush();
while ((line = reader.readLine ()) != null) {
System.out.println ("Stdout: " + line);
}
input = scan.nextLine();
input += "\n";
writer.write(input);
writer.close();
while ((line = reader.readLine ()) != null) {
System.out.println ("Stdout: " + line);
}
Antworten:
Erstens würde ich empfehlen, die Leitung zu ersetzen
mit den Zeilen
ProcessBuilder ist neu in Java 5 und erleichtert das Ausführen externer Prozesse. Meiner Meinung nach besteht die wichtigste Verbesserung
Runtime.getRuntime().exec()
darin, dass Sie den Standardfehler des untergeordneten Prozesses in seine Standardausgabe umleiten können. Dies bedeutet, dass Sie nur einenInputStream
zum Lesen haben. Zuvor mussten zwei separate Threads vorhanden sein, ein Lesevorgang vonstdout
und ein Lesevorgang vonstderr
, um zu vermeiden, dass der Standardfehlerpuffer gefüllt wird, während der Standardausgabepuffer leer ist (wodurch der untergeordnete Prozess hängen bleibt), oder umgekehrt.Als nächstes die Schleifen (von denen Sie zwei haben)
Beenden Sie
reader
das Programm nur, wenn das , das aus der Standardausgabe des Prozesses liest, das Dateiende zurückgibt. Dies geschieht nur, wenn derbash
Prozess beendet wird. Es wird kein Dateiende zurückgegeben, wenn derzeit keine Ausgabe mehr vom Prozess erfolgt. Stattdessen wartet es auf die nächste Ausgabezeile des Prozesses und kehrt erst zurück, wenn es diese nächste Zeile hat.Da Sie zwei Eingabezeilen an den Prozess senden, bevor Sie diese Schleife erreichen, bleibt die erste dieser beiden Schleifen hängen, wenn der Prozess nach diesen beiden Eingabezeilen nicht beendet wurde. Es wird dort sitzen und darauf warten, dass eine andere Zeile gelesen wird, aber es wird nie eine andere Zeile geben, in der es gelesen werden kann.
Ich Ihren Quellcode kompiliert (Ich bin auf Windows zur Zeit, so dass ich ersetzt
/bin/bash
mitcmd.exe
, aber die Prinzipien sollten gleich sein), und ich stellte fest , dass:echo test
und dannexit
, verlässt das Programm die erste Schleife seit demcmd.exe
Beenden des Prozesses. Das Programm fordert dann eine weitere Eingabezeile an (die ignoriert wird), überspringt direkt die zweite Schleife, da der untergeordnete Prozess bereits beendet wurde, und beendet sich dann selbst.exit
dannecho test
tippe, erhalte ich eine IOException, die sich über das Schließen einer Pipe beschwert. Dies ist zu erwarten - die erste Eingabezeile hat den Prozess beendet, und die zweite Zeile kann nirgendwo gesendet werden.Ich habe in einem Programm, an dem ich früher gearbeitet habe, einen Trick gesehen, der etwas Ähnliches bewirkt, wie Sie es zu wollen scheinen. Dieses Programm behielt eine Reihe von Shells bei, führte darin Befehle aus und las die Ausgabe dieser Befehle. Der verwendete Trick bestand darin, immer eine "magische" Zeile zu schreiben, die das Ende der Ausgabe des Shell-Befehls markiert, und damit zu bestimmen, wann die Ausgabe des an die Shell gesendeten Befehls beendet war.
Ich habe Ihren Code genommen und alles nach der zugewiesenen Zeile
writer
durch die folgende Schleife ersetzt:Danach konnte ich zuverlässig einige Befehle ausführen und die Ausgabe von jedem einzeln an mich zurücksenden lassen.
Die beiden
echo --EOF--
Befehle in der an die Shell gesendeten Zeile dienen dazu, sicherzustellen, dass die Ausgabe des Befehls--EOF--
auch im Ergebnis eines Befehlsfehlers beendet wird.Natürlich hat dieser Ansatz seine Grenzen. Diese Einschränkungen umfassen:
--EOF--
.bash
meldet einen Syntaxfehler und wird beendet, wenn Sie Text mit einem nicht übereinstimmenden Text eingeben)
.Diese Punkte sind für Sie möglicherweise nicht von Bedeutung, wenn sich das, was Sie als geplante Aufgabe ausführen möchten, auf einen Befehl oder einen kleinen Satz von Befehlen beschränkt, die sich niemals so pathologisch verhalten werden.
BEARBEITEN : Verbessern Sie das Exit-Handling und andere kleinere Änderungen, nachdem Sie dies unter Linux ausgeführt haben.
quelle
Ich denke, Sie können Thread wie Dämonen-Thread zum Lesen Ihrer Eingabe verwenden und Ihr Ausgabeleser befindet sich bereits in der while-Schleife im Haupt-Thread, sodass Sie gleichzeitig lesen und schreiben können. Sie können Ihr Programm folgendermaßen ändern:
und Sie können Leser wird wie oben sein, dh
Machen Sie Ihren Autor als endgültig, sonst ist er für die innere Klasse nicht zugänglich.
quelle
Sie haben
writer.close();
in Ihrem Code. Bash erhält also EOFstdin
und beendet das Programm. Dann bekommen SieBroken pipe
beim Versuch, aus der nicht mehr existierendenstdout
Bash zu lesen .quelle