Estoy construyendo un proceso en Java usando ProcessBuilder de la siguiente manera:
ProcessBuilder pb = new ProcessBuilder()
.command("somecommand", "arg1", "arg2")
.redirectErrorStream(true);
Process p = pb.start();
InputStream stdOut = p.getInputStream();
Ahora mi problema es el siguiente: me gustaría capturar lo que esté pasando por stdout y / o stderr de ese proceso y redirigirlo de forma System.out
asincrónica. Quiero que el proceso y su redirección de salida se ejecuten en segundo plano. Hasta ahora, la única forma que he encontrado para hacer esto es generar manualmente un nuevo hilo que leerá continuamente stdOut
y luego llamará al write()
método apropiado de 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();
Si bien ese enfoque funciona, se siente un poco sucio. Y además de eso, me da un hilo más para administrar y terminar correctamente. ¿Hay alguna forma mejor de hacer esto?
java
processbuilder
Señor de los cerdos
fuente
fuente
org.apache.commons.io.IOUtils.copy(new ProcessBuilder().command(commandLine) .redirectErrorStream(true).start().getInputStream(), System.out);
Respuestas:
La única forma en Java 6 o anterior es con un llamado
StreamGobbler
(que ya está empezando a crear):...
Para Java 7, consulte la respuesta de Evgeniy Dorofeev.
fuente
Utilícelo
ProcessBuilder.inheritIO
, establece el origen y el destino de la E / S estándar del subproceso para que sean los mismos que los del proceso Java actual.Si Java 7 no es una opción
Los subprocesos morirán automáticamente cuando finalice el subproceso, porque lo
src
hará EOF.fuente
inheritIO()
uno de esosredirect*(ProcessBuilder.Redirect)
métodos útiles la próxima vez que necesite hacerlo en un proyecto de Java 7. Desafortunadamente mi proyecto es java 6.sc
necesita estar cerrado?Una solución flexible con Java 8 lambda que le permite proporcionar una
Consumer
que procesará la salida (por ejemplo, registrarla) línea por línea.run()
es una línea de una sola línea sin excepciones marcadas lanzadas. Alternativamente a la implementaciónRunnable
, puede extenderseThread
como sugieren otras respuestas.Luego puede usarlo, por ejemplo, así:
Aquí, el flujo de salida se redirige a
System.out
y el flujo de error se registra en el nivel de error mediantelogger
.fuente
forEach()
en elrun()
método se bloqueará hasta que la secuencia esté abierta, esperando la siguiente línea. Saldrá cuando la corriente esté cerrada.Es tan simple como seguir:
por .redirectErrorStream (verdadero) le dice al proceso que combine el error y el flujo de salida y luego por .redirectOutput (archivo) redirige la salida combinada a un archivo.
Actualizar:
Me las arreglé para hacer esto de la siguiente manera:
Ahora puede ver las salidas de los subprocesos principal y asyncOut en System.out
fuente
Solución java8 simple con captura de ambas salidas y procesamiento reactivo usando
CompletableFuture
:Y el uso:
fuente
Hay una biblioteca que proporciona un ProcessBuilder mejor, zt-exec. Esta biblioteca puede hacer exactamente lo que está pidiendo y más.
Así es como se vería su código con zt-exec en lugar de ProcessBuilder:
agregue la dependencia:
El código :
La documentación de la biblioteca está aquí: https://github.com/zeroturnaround/zt-exec/
fuente
Yo también puedo usar solo Java 6. Usé la implementación del escáner de subprocesos de @ EvgeniyDorofeev. En mi código, después de que finaliza un proceso, tengo que ejecutar inmediatamente otros dos procesos que comparan la salida redirigida (una prueba unitaria basada en diferencias para garantizar que stdout y stderr sean los mismos que los bendecidos).
Los subprocesos del escáner no terminan lo suficientemente pronto, incluso si espero () que se complete el proceso. Para que el código funcione correctamente, tengo que asegurarme de que los hilos se unan después de que finalice el proceso.
fuente
Como adición a la respuesta de msangel, me gustaría agregar el siguiente bloque de código:
Permite redirigir el flujo de entrada (stdout, stderr) del proceso a algún otro consumidor. Este podría ser System.out :: println o cualquier otra cosa que consuma cadenas.
Uso:
fuente
Su código personalizado va en lugar del
...
fuente
De forma predeterminada, el subproceso creado no tiene su propia terminal o consola. Todas sus operaciones de E / S estándar (es decir, stdin, stdout, stderr) serán redirigidas al proceso padre, donde se puede acceder a ellas a través de las secuencias obtenidas mediante los métodos getOutputStream (), getInputStream () y getErrorStream (). El proceso padre utiliza estos flujos para alimentar la entrada y obtener la salida del subproceso. Debido a que algunas plataformas nativas solo proporcionan un tamaño de búfer limitado para los flujos de entrada y salida estándar, si no se escribe rápidamente el flujo de entrada o se lee el flujo de salida del subproceso, es posible que el subproceso se bloquee o incluso se bloquee.
https://www.securecoding.cert.org/confluence/display/java/FIO07-J.+Do+not+let+external+processes+block+on+IO+buffers
fuente