Ich erstelle einen Prozess in Java mit ProcessBuilder wie folgt:
ProcessBuilder pb = new ProcessBuilder()
.command("somecommand", "arg1", "arg2")
.redirectErrorStream(true);
Process p = pb.start();
InputStream stdOut = p.getInputStream();
Jetzt ist mein Problem das Folgende: Ich möchte erfassen, was durch stdout und / oder stderr dieses Prozesses läuft, und es System.out
asynchron umleiten . Ich möchte, dass der Prozess und seine Ausgabeumleitung im Hintergrund ausgeführt werden. Bisher habe ich nur die Möglichkeit gefunden, manuell einen neuen Thread zu erstellen, der kontinuierlich aus liest stdOut
und dann die entsprechende write()
Methode von aufruft System.out
.
new Thread(new Runnable(){
public void run(){
byte[] buffer = new byte[8192];
int len = -1;
while((len = stdOut.read(buffer)) > 0){
System.out.write(buffer, 0, len);
}
}
}).start();
Während dieser Ansatz funktioniert, fühlt er sich ein bisschen schmutzig an. Und obendrein gibt es mir noch einen Thread, den ich richtig verwalten und beenden kann. Gibt es einen besseren Weg, dies zu tun?
java
processbuilder
LordOfThePigs
quelle
quelle
org.apache.commons.io.IOUtils.copy(new ProcessBuilder().command(commandLine) .redirectErrorStream(true).start().getInputStream(), System.out);
Antworten:
Nur in Java 6 oder früher gibt es einen so genannten
StreamGobbler
(den Sie gerade erstellen):...
Für Java 7 siehe Evgeniy Dorofeevs Antwort.
quelle
Bei Verwendung
ProcessBuilder.inheritIO
werden die Quelle und das Ziel für die Standardprozess-E / A so festgelegt, dass sie mit denen des aktuellen Java-Prozesses übereinstimmen.Wenn Java 7 keine Option ist
Threads sterben automatisch ab, wenn der Unterprozess abgeschlossen ist, da
src
EOF.quelle
inheritIO()
oder eine dieser praktischenredirect*(ProcessBuilder.Redirect)
Methoden verwenden, wenn ich das für ein Java 7-Projekt tun muss. Leider ist mein Projekt Java 6.sc
muss geschlossen werden?Eine flexible Lösung mit Java 8 Lambda, mit der Sie eine bereitstellen können
Consumer
, die die Ausgabe zeilenweise verarbeitet (z. B. protokolliert).run()
ist ein Einzeiler ohne aktivierte Ausnahmen. Alternativ zur ImplementierungRunnable
kann es erweitertThread
werden, wie andere Antworten vermuten lassen.Sie können es dann zum Beispiel so verwenden:
Hier wird der Ausgabestream umgeleitet
System.out
und der Fehlerstrom auf der Fehlerebene von der protokolliertlogger
.quelle
forEach()
in derrun()
Methode wird blockiert, bis der Stream geöffnet ist und auf die nächste Zeile wartet. Es wird beendet, wenn der Stream geschlossen wird.Es ist so einfach wie folgt:
Mit .redirectErrorStream (true) weisen Sie den Prozess an, Fehler und Ausgabestream zusammenzuführen, und mit .redirectOutput (Datei) leiten Sie die zusammengeführte Ausgabe in eine Datei um.
Aktualisieren:
Ich habe das wie folgt geschafft:
Jetzt können Sie beide Ausgaben von Haupt- und AsyncOut-Threads in System.out sehen
quelle
Einfache Java8-Lösung mit Erfassung beider Ausgaben und reaktiver Verarbeitung mit
CompletableFuture
:Und die Verwendung:
quelle
Es gibt eine Bibliothek, die einen besseren ProcessBuilder bietet, zt-exec. Diese Bibliothek kann genau das tun, wonach Sie fragen und mehr.
So würde Ihr Code mit zt-exec anstelle von ProcessBuilder aussehen:
Fügen Sie die Abhängigkeit hinzu:
Der Code :
Die Dokumentation der Bibliothek finden Sie hier: https://github.com/zeroturnaround/zt-exec/
quelle
Auch ich kann nur Java 6 verwenden. Ich habe die Thread-Scanner-Implementierung von @ EvgeniyDorofeev verwendet. In meinem Code muss ich nach Abschluss eines Prozesses sofort zwei weitere Prozesse ausführen, die jeweils die umgeleitete Ausgabe vergleichen (ein diff-basierter Komponententest, um sicherzustellen, dass stdout und stderr mit den gesegneten identisch sind).
Die Scanner-Threads werden nicht früh genug beendet, selbst wenn ich darauf warte, dass () der Vorgang abgeschlossen ist. Damit der Code korrekt funktioniert, muss ich sicherstellen, dass die Threads nach Abschluss des Prozesses verbunden werden.
quelle
Als Ergänzung zur Antwort von msangel möchte ich den folgenden Codeblock hinzufügen:
Es ermöglicht die Umleitung des Eingabestreams (stdout, stderr) des Prozesses an einen anderen Verbraucher. Dies kann System.out :: println oder etwas anderes sein, das Zeichenfolgen verbraucht.
Verwendung:
quelle
Ihr benutzerdefinierter Code wird anstelle von
...
quelle
Standardmäßig verfügt der erstellte Unterprozess nicht über ein eigenes Terminal oder eine eigene Konsole. Alle Standard-E / A-Operationen (dh stdin, stdout, stderr) werden an den übergeordneten Prozess umgeleitet, wo auf sie über die Streams zugegriffen werden kann, die mit den Methoden getOutputStream (), getInputStream () und getErrorStream () abgerufen wurden. Der übergeordnete Prozess verwendet diese Streams, um Eingaben in den Unterprozess einzuspeisen und von diesen abzurufen. Da einige native Plattformen nur eine begrenzte Puffergröße für Standardeingabe- und -ausgabestreams bereitstellen, kann das Versäumnis, den Eingabestream sofort zu schreiben oder den Ausgabestream des Unterprozesses zu lesen, dazu führen, dass der Unterprozess blockiert oder sogar blockiert.
https://www.securecoding.cert.org/confluence/display/java/FIO07-J.+Do+not+let+external+processes+block+on+IO+buffers
quelle