2010-11-25 19 views
7

Utilizo el método Runtime exec() para crear un subproceso en Java. Sin embargo, dado que el subproceso es un programa interactivo, debo proporcionarle información cuando así lo requiera. También necesito mostrar el resultado del subproceso. ¿Cómo puedo hacer esto de la manera más simple posible?Ejecutar un subproceso, proporcionar entrada y salida correctamente en Java

Estaba usando un StreamGobbler para mostrar el resultado del programa usando process.getInputStream(). Yo, sin embargo, no sé cómo identificar cuándo el programa está esperando la entrada y cuándo proporcionarlo usando proc.getOutputStream. ¿Cómo puedo hacer esto?

Respuesta

2

tiene que copiar la entrada y salida entre las corrientes del subproceso y System arroyos (System.in, System.out y System.err). Esto está relacionado con my recent quesion. La mejor solución que he encontrado hasta ahora es:

import java.io.FileInputStream; 
import java.io.FilterInputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.lang.reflect.Field; 
import java.nio.ByteBuffer; 
import java.nio.channels.AsynchronousCloseException; 
import java.nio.channels.FileChannel; 

class StreamCopier implements Runnable { 
    private InputStream in; 
    private OutputStream out; 

    public StreamCopier(InputStream in, OutputStream out) { 
     this.in = in; 
     this.out = out; 
    } 

    public void run() { 
     try { 
      int n; 
      byte[] buffer = new byte[4096]; 
      while ((n = in.read(buffer)) != -1) { 
       out.write(buffer, 0, n); 
       out.flush(); 
      } 
     } 
     catch (IOException e) { 
      System.out.println(e); 
     } 
    } 
} 

class InputCopier implements Runnable { 
    private FileChannel in; 
    private OutputStream out; 

    public InputCopier(FileChannel in, OutputStream out) { 
     this.in = in; 
     this.out = out; 
    } 

    public void run() { 
     try { 
      int n; 
      ByteBuffer buffer = ByteBuffer.allocate(4096); 
      while ((n = in.read(buffer)) != -1) { 
       out.write(buffer.array(), 0, n); 
       out.flush(); 
      } 
      out.close(); 
     } 
     catch (AsynchronousCloseException e) {} 
     catch (IOException e) { 
      System.out.println(e); 
     } 
    } 
} 

public class Test { 
    private static FileChannel getChannel(InputStream in) 
      throws NoSuchFieldException, IllegalAccessException { 
     Field f = FilterInputStream.class.getDeclaredField("in"); 
     f.setAccessible(true); 
     while (in instanceof FilterInputStream) 
      in = (InputStream)f.get((FilterInputStream)in); 
     return ((FileInputStream)in).getChannel(); 
    } 

    public static void main(String[] args) 
      throws IOException, InterruptedException, 
        NoSuchFieldException, IllegalAccessException { 
     Process process = Runtime.getRuntime().exec("sh -i +m"); 
     Thread outThread = new Thread(new StreamCopier(
       process.getInputStream(), System.out)); 
     outThread.start(); 
     Thread errThread = new Thread(new StreamCopier(
       process.getErrorStream(), System.err)); 
     errThread.start(); 
     Thread inThread = new Thread(new InputCopier(
       getChannel(System.in), process.getOutputStream())); 
     inThread.start(); 
     process.waitFor(); 
     System.in.close(); 
     outThread.join(); 
     errThread.join(); 
     inThread.join(); 
    } 
} 

La parte difícil aquí es extraer un canal de System.in. Sin esto, no podrá interrumpir el hilo que lee la entrada cuando finaliza el subproceso.

Este enfoque tiene un serio inconveniente: después de cerrar System.in ya no se puede leer de él. La solución alternativa que estoy usando actualmente es tener un único hilo de redirección de entrada utilizado para todos los subprocesos.

2

Pregúntese "¿Cómo sé cuando el programa quiere entrada cuando lo ejecuto desde la línea de comandos"? Verá lo que indica e ingresará datos según ese aviso. El principio será el mismo, excepto que su código deberá interpretar la salida del programa y proporcionar la entrada correcta.

Para evitar reinventar la rueda, echar un vistazo a ExpectJ y/o Expect4J, que son implementaciones de Java del venerable * nix herramienta Expect, que está diseñado para manejar este tipo de interacción programática.

+0

Tuve un problema similar y lo resolví usando ExpectJ. Una biblioteca muy intuitiva, me tomó como 5 minutos para que funcione. Buen consejo +1 – codelidoo

Cuestiones relacionadas