Pregunta

Yo uso el método de ejecución exec () para crear un subproceso en Java. Sin embargo, dado que el subproceso es un programa interactivo, necesito hacer aportaciones a ella como y cuando sea necesario por ella. También necesito para mostrar la salida del subproceso. ¿Cómo puedo hacer esto de la manera más sencilla posible

I estaba usando un StreamGobbler para mostrar la salida del programa usando process.getInputStream (). Yo, sin embargo, no sé cómo identificar cuando el programa está a la espera para la entrada y cuando se le proporcione de entrada utilizando proc.getOutputStream. ¿Cómo puedo hacer esto?

¿Fue útil?

Solución

Debe copiar la entrada y salida entre las corrientes de subproceso y arroyos System (System.in, System.out y System.err). Esto se relaciona con mi quesion reciente . 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 será capaz de interrumpir el hilo que lee la entrada termina cuando los subprocesos.

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

Otros consejos

Pregúntese "¿Cómo sé cuando el programa quiere de entrada al ejecutarlo desde la línea de comandos"? Ves lo que incita e introduzca los datos basados ??en dicha solicitud. El principio será el mismo, excepto el código tendrá que 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 esperar herramienta, que está diseñado para manejar este tipo de interacción programática.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top