来自apache-commons exec的进程输出

我在这里结束了我的智慧。 我确信这很简单,我很可能在理解java和流时遇到很大漏洞。 我认为有这么多课程,我试图通过API来弄清楚我何时以及如何使用大量的输入/输出流,我有点不知所措。

我刚刚了解了apache commons库的存在(自学java失败),我正在尝试将我的一些Runtime.getRuntime()。exec转换为使用commons – exec。 已经修复了一些每6个月一次这个问题的问题,然后消除了exec的风格问题。

代码执行perl脚本,并在GUI运行时显示GUI中脚本的stdout。

调用代码在swingworker内部。

我迷路了如何使用pumpStreamHandler ……无论如何这里是旧代码:

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(); } 

我想这就是我在很久以前不完全理解的复制粘贴代码。 上面我假设正在执行该过程,然后抓取输出流(通过“getInputStream”?),将其放入缓冲读取器,然后将循环直到缓冲区为空。

我没有得到的是为什么这里不需要’waitfor’样式命令? 是否可能有一段时间缓冲区将为空,退出循环,并在进程仍在进行时继续? 当我运行它时,情况似乎并非如此。

无论如何,我试图使用commons exec获得相同的行为,基本上再次从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(); 

我想弄清楚pumpstreamhandler在做什么。 我假设这将从exec对象获取输出,并填充OutputStream我提供它与perl脚本的stdout / err中的字节?

如果是这样,您将如何获得上述行为让它逐行流输出? 在示例中,人们显示最后调用out.toString(),并且我认为这只会在脚本运行完毕后从脚本中转储所有输出? 你会如何做到它会显示输出,因为它是逐行运行的?

————未来编辑———————

通过谷歌找到这个并且工作得很好:

 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()); } 

不要将ByteArrayOutputStream传递给PumpStreamHandlerPumpStreamHandler使用抽象类org.apache.commons.exec.LogOutputStream 。 像这样的东西:

 import java.util.LinkedList; import java.util.List; import org.apache.commons.exec.LogOutputStream; public class CollectingLogOutputStream extends LogOutputStream { private final List lines = new LinkedList(); @Override protected void processLine(String line, int level) { lines.add(line); } public List getLines() { return lines; } } 

然后在对exec.execute进行阻塞调用之后,你的getLines()会出现你正在寻找的标准输出和标准错误。 从执行进程的角度来看, ExecutionResultHandler是可选的,并将所有stdOut / stdErr收集到一个行列表中。

我没有得到的是为什么这里不需要’waitfor’样式命令? 是否可能有一段时间缓冲区将为空,退出循环,并在进程仍在进行时继续? 当我运行它时,情况似乎并非如此。

readLine块。 也就是说,您的代码将等到读取一行。

PumpStreamHandler

来自文档

将标准输出和子过程的错误复制到标准输出和父进程的错误。 如果输出或错误流设置为null,则该流的任何反馈都将丢失。

根据James A Wilson的回答,我创建了辅助类“Execute”。 它将他的答案包含在一个解决方案中,该解决方案还提供exitValue以方便使用。

以这种方式执行命令需要一行:

ExecResult result=Execute.execCmd(cmd,expectedExitCode);

以下Junit Testcase测试并演示如何使用它:

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 lines = result.getLines(); assertTrue(lines.size()>0); for (String line:lines) { System.out.println(line); } } } 

执行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:  org.apache.commons commons-exec 1.2  * @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://stackoverflow.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 lines = new LinkedList(); @Override protected void processLine(String line, int level) { lines.add(line); } public List 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; } }