En primer lugar, recomendaría reemplazar la línea
Process process = Runtime.getRuntime ().exec ("/bin/bash");
con las lineas
ProcessBuilder builder = new ProcessBuilder("/bin/bash");
builder.redirectErrorStream(true);
Process process = builder.start();
ProcessBuilder es nuevo en Java 5 y facilita la ejecución de procesos externos. En mi opinión, su mejora más significativa Runtime.getRuntime().exec()
es que le permite redirigir el error estándar del proceso hijo a su salida estándar. Esto significa que solo tienes unoInputStream
para leer. Antes de esto, necesitaba tener dos subprocesos separados, uno de lectura stdout
y otro de lectura stderr
, para evitar que el búfer de error estándar se llene mientras el búfer de salida estándar estaba vacío (lo que provocaba que el proceso hijo se bloqueara), o viceversa.
A continuación, los bucles (de los cuales tienes dos)
while ((line = reader.readLine ()) != null) {
System.out.println ("Stdout: " + line);
}
solo salir cuando el reader
, que lee de la salida estándar del proceso, devuelve el final del archivo. Esto solo sucede cuando el bash
proceso finaliza. No devolverá el final del archivo si actualmente no hay más resultados del proceso. En su lugar, esperará la siguiente línea de salida del proceso y no regresará hasta que tenga la siguiente línea.
Como está enviando dos líneas de entrada al proceso antes de llegar a este bucle, el primero de estos dos bucles se bloqueará si el proceso no ha salido después de estas dos líneas de entrada. Se quedará allí esperando a que se lea otra línea, pero nunca habrá otra línea para leer.
Compilé su código fuente (estoy en Windows en este momento, así que lo reemplacé /bin/bash
concmd.exe
, pero los principios deben ser los mismos), y me encontré con lo siguiente:
- después de escribir dos líneas, aparece la salida de los dos primeros comandos, pero luego el programa se cuelga,
- si escribo, digamos,
echo test
y luego exit
, el programa sale del primer ciclo desde que el cmd.exe
proceso ha finalizado. Luego, el programa solicita otra línea de entrada (que se ignora), salta directamente el segundo ciclo ya que el proceso hijo ya ha salido y luego sale por sí mismo.
- si escribo
exit
y luego echo test
, obtengo una IOException quejándose de que una tubería está cerrada. Esto es de esperar: la primera línea de entrada provocó la salida del proceso y no hay ningún lugar para enviar la segunda línea.
He visto un truco que hace algo similar a lo que parece querer, en un programa en el que solía trabajar. Este programa mantuvo alrededor de una serie de shells, ejecutó comandos en ellos y leyó la salida de estos comandos. El truco utilizado fue escribir siempre una línea "mágica" que marque el final de la salida del comando de shell y usarla para determinar cuándo había terminado la salida del comando enviado al shell.
Tomé su código y reemplacé todo después de la línea que se asigna writer
con el siguiente bucle:
while (scan.hasNext()) {
String input = scan.nextLine();
if (input.trim().equals("exit")) {
// Putting 'exit' amongst the echo --EOF--s below doesn't work.
writer.write("exit\n");
} else {
writer.write("((" + input + ") && echo --EOF--) || echo --EOF--\n");
}
writer.flush();
line = reader.readLine();
while (line != null && ! line.trim().equals("--EOF--")) {
System.out.println ("Stdout: " + line);
line = reader.readLine();
}
if (line == null) {
break;
}
}
Después de hacer esto, podría ejecutar de manera confiable algunos comandos y hacer que la salida de cada uno me regresara individualmente.
Los dos echo --EOF--
comandos en la línea enviados al shell están ahí para garantizar que la salida del comando termine --EOF--
incluso en el resultado de un error del comando.
Por supuesto, este enfoque tiene sus limitaciones. Estas limitaciones incluyen:
- si ingreso un comando que espera la entrada del usuario (por ejemplo, otro shell), el programa parece bloquearse,
- asume que cada proceso ejecutado por el shell finaliza su salida con una nueva línea,
- se confunde un poco si el comando que está ejecutando el shell escribe una línea
--EOF--
.
bash
informa un error de sintaxis y sale si ingresa algún texto con un )
.
Es posible que estos puntos no le importen si lo que sea que esté pensando en ejecutar como una tarea programada se limitará a un comando o un pequeño conjunto de comandos que nunca se comportarán de forma tan patológica.
EDITAR : mejore el manejo de salidas y otros cambios menores después de ejecutar esto en Linux.