2010-11-14 12 views
7

Esto es una continuación de this question. La respuesta sugiere que hayCómo copiar flujos de entrada/salida del proceso a sus contrapartidas del sistema?

copiar el Proceso cabo, se equivocan, y los flujos de entrada a las versiones del sistema

con IOUtils.copy de la siguiente manera (después de la fijación de diversos errores de compilación):

import org.apache.commons.io.IOUtils; 
import java.io.IOException; 

public class Test { 
    public static void main(String[] args) 
      throws IOException, InterruptedException { 
     final Process process = Runtime.getRuntime().exec("/bin/sh -i"); 
     new Thread(new Runnable() {public void run() { 
      try { 
       IOUtils.copy(process.getInputStream(), System.out); 
      } catch (IOException e) {} 
     } }).start(); 
     new Thread(new Runnable() {public void run() { 
      try { 
       IOUtils.copy(process.getErrorStream(), System.err); 
      } catch (IOException e) {} 
     } }).start(); 
     new Thread(new Runnable() {public void run() { 
      try { 
       IOUtils.copy(System.in, process.getOutputStream()); 
      } catch (IOException e) {} 
     } }).start(); 
     process.waitFor(); 
    } 
} 

Sin embargo, el código resultante no funciona para procesos interactivos como el que ejecuta el comando sh -i. En el último caso, no hay respuesta a ninguno de los comandos sh.

Así que mi pregunta es: ¿podría sugerir una alternativa para copiar las transmisiones que funcionarán con los procesos interactivos?

Respuesta

5

El problema es que IOUtil.copy() se está ejecutando mientras hay datos en el InputStream que se copiarán. Dado que su proceso solo produce datos de vez en cuando, IOUtil.copy() sale porque cree que no hay datos para copiar.

Sólo tienes que copiar los datos a mano y usar un booleano para detener la forma de la rosca exterior:

byte[] buf = new byte[1024]; 
int len; 
while (threadRunning) { // threadRunning is a boolean set outside of your thread 
    if((len = input.read(buf)) > 0){ 
     output.write(buf, 0, len); 
    } 
} 

Esto lee en trozos tantos bytes, ya que hay disponibles en flujoEntrada y copia todos ellos a la salida. Internamente, InputStream coloca el hilo en wait() y luego lo activa cuando hay datos disponibles.
Por lo tanto, es tan eficiente como puede tenerlo en esta situación.

+1

Gracias por la sugerencia. Sin embargo, 'IOUtil.copy()' no sale hasta que se llega al final de la secuencia, por lo que no es tan diferente de su código que tiene los mismos problemas con 'sh'. – vitaut

+0

Pls eche un vistazo a este http://stackoverflow.com/questions/804951/is-it-possible-to-read-from-a-java-inputstream-with-a-timeout/808795#808795 –

1

Process.getOutputStream() devuelve un BufferedOutputStream, así que si usted quiere que su entrada para realmente llegar a la sub-proceso que tiene que llamar a flush() después de cada write().

También puede reescribir su ejemplo para hacer todo en un hilo (aunque se utiliza el sondeo de leer tanto System.in y el proceso de salida estándar al mismo tiempo):

import java.io.*; 

public class TestProcessIO { 

    public static boolean isAlive(Process p) { 
    try { 
     p.exitValue(); 
     return false; 
    } 
    catch (IllegalThreadStateException e) { 
     return true; 
    } 
    } 

    public static void main(String[] args) throws IOException { 
    ProcessBuilder builder = new ProcessBuilder("bash", "-i"); 
    builder.redirectErrorStream(true); // so we can ignore the error stream 
    Process process = builder.start(); 
    InputStream out = process.getInputStream(); 
    OutputStream in = process.getOutputStream(); 

    byte[] buffer = new byte[4000]; 
    while (isAlive(process)) { 
     int no = out.available(); 
     if (no > 0) { 
     int n = out.read(buffer, 0, Math.min(no, buffer.length)); 
     System.out.println(new String(buffer, 0, n)); 
     } 

     int ni = System.in.available(); 
     if (ni > 0) { 
     int n = System.in.read(buffer, 0, Math.min(ni, buffer.length)); 
     in.write(buffer, 0, n); 
     in.flush(); 
     } 

     try { 
     Thread.sleep(10); 
     } 
     catch (InterruptedException e) { 
     } 
    } 

    System.out.println(process.exitValue()); 
    } 
} 
1

su lugar, debe utilizar el ProcessBuilder.redirectOutput método & amigos. Leer más here

Cuestiones relacionadas