Tengo el siguiente ejemplo de código a continuación. Por lo que puede ingresar un comando en el shell bash, es decir, echo test
y hacer que el resultado se haga eco. Sin embargo, después de la primera lectura. ¿Otros flujos de salida no funcionan?
¿Por qué es esto o estoy haciendo algo mal? Mi objetivo final es crear una tarea programada subprocesada que ejecute un comando periódicamente para / bash para que OutputStream
y InputStream
tengan que trabajar en conjunto y no dejar de funcionar. También he estado experimentando el error ¿ java.io.IOException: Broken pipe
alguna idea?
Gracias.
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);
}
Respuestas:
En primer lugar, recomendaría reemplazar la línea
con las lineas
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 lecturastdout
y otro de lecturastderr
, 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)
solo salir cuando el
reader
, que lee de la salida estándar del proceso, devuelve el final del archivo. Esto solo sucede cuando elbash
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:echo test
y luegoexit
, el programa sale del primer ciclo desde que elcmd.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.exit
y luegoecho 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: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:
--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.
fuente
Creo que puede usar un hilo como demon-thread para leer su entrada y su lector de salida ya estará en el bucle while en el hilo principal para que pueda leer y escribir al mismo tiempo. Puede modificar su programa de esta manera:
y puede que el lector sea el mismo que el anterior, es decir
Haga que su escritor sea definitivo, de lo contrario, no podrá acceder a él la clase interna.
fuente
Tienes
writer.close();
en tu código. Entonces, bash recibe EOFstdin
y sale. Entonces obtienesBroken pipe
al intentar leer desdestdout
el difunto bash.fuente