java - Procesar la salida de apache-commons exec
apache-commons-exec (3)
Lo que no entiendo es por qué no hay necesidad de un comando de estilo ''esperar'' aquí. ¿No es posible que haya algún tiempo en el que el búfer esté vacío, salga del bucle y continúe mientras el proceso continúa? Cuando lo ejecuto, este no parece ser el caso.
Bloques readLine. Es decir, su código esperará hasta que se haya leído una línea.
PumpStreamHandler
de la Documentation
Copia la salida estándar y el error de los subprocesos en la salida estándar y el error del proceso principal. Si el flujo de salida o de error se establece en nulo, se perderá cualquier comentario de ese flujo.
Estoy en mi ingenio final aquí. Estoy seguro de que esto es algo simple y lo más probable es que tenga grandes agujeros en mi comprensión de java y flujos. Creo que hay tantas clases que estoy un poco abrumado por tratar de revisar la API para averiguar cuándo y cómo quiero usar la multitud de flujos de entrada / salida.
Acabo de enterarme de la existencia de la biblioteca de apache commons (autoaprendizaje de java), y actualmente estoy intentando convertir algunos de mis Runtime.getRuntime (). Exec para usar commons - exec. Ya se ha solucionado algunos de los problemas que, una vez cada 6 meses, surgen y luego desaparecen los problemas de estilo con el ejecutivo.
El código ejecuta un script en perl y muestra la salida estándar del script en la GUI mientras se ejecuta.
El código de llamada está dentro de un columpio.
Me estoy perdiendo cómo usar pumpStreamHandler ... de todos modos aquí está el código antiguo:
String pl_cmd = "perl script.pl"
Process p_pl = Runtime.getRuntime().exec( pl_cmd );
BufferedReader br_pl = new BufferedReader( new InputStreamReader( p_pl.getInputStream() ) );
stdout = br_pl.readLine();
while ( stdout != null )
{
output.displayln( stdout );
stdout = br_pl.readLine();
}
Supongo que esto es lo que obtengo por copiar código que no entiendo completamente hace mucho tiempo. Supongo que lo anterior es que está ejecutando el proceso, luego toma el flujo de salida (a través de "getInputStream"?), Lo coloca en un lector almacenado en búfer, y luego hará un bucle allí hasta que el búfer esté vacío.
Lo que no entiendo es por qué no hay necesidad de un comando de estilo ''esperar'' aquí. ¿No es posible que haya algún tiempo en el que el búfer esté vacío, salga del bucle y continúe mientras el proceso continúa? Cuando lo ejecuto, este no parece ser el caso.
En cualquier caso, estoy tratando de obtener el mismo comportamiento utilizando commons exec, básicamente otra vez a partir del código encontrado de Google:
DefaultExecuteResultHandler rh = new DefaultExecuteResultHandler();
ExecuteWatchdog wd = new ExecuteWatchdog( ExecuteWatchdog.INFINITE_TIMEOUT );
Executor exec = new DefaultExecutor();
ByteArrayOutputStream out = new ByteArrayOutputStream();
PumpStreamHandler psh = new PumpStreamHandler( out );
exec.setStreamHandler( psh );
exec.setWatchdog( wd );
exec.execute(cmd, rh );
rh.waitFor();
Estoy tratando de averiguar qué está haciendo pumpstreamhandler. Supongo que esto tomará la salida del objeto exec y llenará el OutputStream. ¿Lo proporciono con los bytes del stdout / err del script perl?
Si es así, ¿cómo obtendría el comportamiento anterior para que transmita la línea de salida por línea? En los ejemplos, la gente muestra que llama a out.toString () al final, y asumo que esto solo me daría un volcado de toda la salida del script una vez que se haya ejecutado. ¿Cómo lo haría de manera tal que muestre la salida cuando se ejecuta línea por línea?
------------ Edición futura ---------------------
Encontré esto a través de google y funciona bien también:
public static void main(String a[]) throws Exception
{
ByteArrayOutputStream stdout = new ByteArrayOutputStream();
PumpStreamHandler psh = new PumpStreamHandler(stdout);
CommandLine cl = CommandLine.parse("ls -al");
DefaultExecutor exec = new DefaultExecutor();
exec.setStreamHandler(psh);
exec.execute(cl);
System.out.println(stdout.toString());
}
Basándome en la respuesta de James A Wilson, creé la clase de ayuda "Ejecutar". Envuelve su respuesta en una solución que también proporciona exitValue para su comodidad.
Una sola línea es necesaria para ejecutar un comando de esta manera:
ExecResult result=Execute.execCmd(cmd,expectedExitCode);
El siguiente Junit Testcase prueba y muestra cómo usarlo:
Caso de prueba Junit4:
package com.bitplan.newsletter;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.Test;
import com.bitplan.cmd.Execute;
import com.bitplan.cmd.Execute.ExecResult;
/**
* test case for the execute class
* @author wf
*
*/
public class TestExecute {
@Test
public void testExecute() throws Exception {
String cmd="/bin/ls";
ExecResult result = Execute.execCmd(cmd,0);
assertEquals(0,result.getExitCode());
List<String> lines = result.getLines();
assertTrue(lines.size()>0);
for (String line:lines) {
System.out.println(line);
}
}
}
Ejecutar clase auxiliar de Java:
package com.bitplan.cmd;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.LogOutputStream;
import org.apache.commons.exec.PumpStreamHandler;
/**
* Execute helper using apache commons exed
*
* add this dependency to your pom.xml:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
<version>1.2</version>
</dependency>
* @author wf
*
*/
public class Execute {
protected static java.util.logging.Logger LOGGER = java.util.logging.Logger
.getLogger("com.bitplan.cmd");
protected final static boolean debug=true;
/**
* LogOutputStream
* http://.com/questions/7340452/process-output-from
* -apache-commons-exec
*
* @author wf
*
*/
public static class ExecResult extends LogOutputStream {
private int exitCode;
/**
* @return the exitCode
*/
public int getExitCode() {
return exitCode;
}
/**
* @param exitCode the exitCode to set
*/
public void setExitCode(int exitCode) {
this.exitCode = exitCode;
}
private final List<String> lines = new LinkedList<String>();
@Override
protected void processLine(String line, int level) {
lines.add(line);
}
public List<String> getLines() {
return lines;
}
}
/**
* execute the given command
* @param cmd - the command
* @param exitValue - the expected exit Value
* @return the output as lines and exit Code
* @throws Exception
*/
public static ExecResult execCmd(String cmd, int exitValue) throws Exception {
if (debug)
LOGGER.log(Level.INFO,"running "+cmd);
CommandLine commandLine = CommandLine.parse(cmd);
DefaultExecutor executor = new DefaultExecutor();
executor.setExitValue(exitValue);
ExecResult result =new ExecResult();
executor.setStreamHandler(new PumpStreamHandler(result));
result.setExitCode(executor.execute(commandLine));
return result;
}
}
No pase un ByteArrayOutputStream
al PumpStreamHandler
, use una implementación de la clase abstracta org.apache.commons.exec.LogOutputStream
. Algo como esto:
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.exec.LogOutputStream;
public class CollectingLogOutputStream extends LogOutputStream {
private final List<String> lines = new LinkedList<String>();
@Override protected void processLine(String line, int level) {
lines.add(line);
}
public List<String> getLines() {
return lines;
}
}
Luego, después de la llamada de bloqueo a exec.execute
su getLines()
tendrá la salida estándar y el error estándar que está buscando. El ExecutionResultHandler
es opcional desde la perspectiva de solo ejecutar el proceso y recopilar todo el stdOut / stdErr en una lista de líneas.