ProcessBuilder: reenvío stdout y stderr de procesos iniciados sin bloquear el hilo principal
Estoy creando 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 todo 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 encima me da un hilo más para gestionar y terminar correctamente. ¿Hay alguna mejor manera de hacer esto?
Use ProcessBuilder.inheritIO
, establece el origen y el destino para la E/S estándar del subproceso para que sean los mismos que los del proceso Java actual.
Process p = new ProcessBuilder().inheritIO().command("command1").start();
Si Java 7 no es una opción
public static void main(String[] args) throws Exception {
Process p = Runtime.getRuntime().exec("cmd /c dir");
inheritIO(p.getInputStream(), System.out);
inheritIO(p.getErrorStream(), System.err);
}
private static void inheritIO(final InputStream src, final PrintStream dest) {
new Thread(new Runnable() {
public void run() {
Scanner sc = new Scanner(src);
while (sc.hasNextLine()) {
dest.println(sc.nextLine());
}
}
}).start();
}
Los subprocesos morirán automáticamente cuando finalice el subproceso, porque src
EOF.
Para Java 7 y posteriores , consulte la respuesta de Evgeniy Dorofeev .
Para Java 6 y versiones anteriores , cree y utilice un StreamGobbler
:
StreamGobbler errorGobbler =
new StreamGobbler(p.getErrorStream(), "ERROR");
// any output?
StreamGobbler outputGobbler =
new StreamGobbler(p.getInputStream(), "OUTPUT");
// start gobblers
outputGobbler.start();
errorGobbler.start();
...
private class StreamGobbler extends Thread {
InputStream is;
String type;
private StreamGobbler(InputStream is, String type) {
this.is = is;
this.type = type;
}
@Override
public void run() {
try {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null)
System.out.println(type + "> " + line);
}
catch (IOException ioe) {
ioe.printStackTrace();
}
}
}