我在这里结束了我的智慧。我确信这很简单,我很可能在理解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获得相同的行为,基本上再次从谷歌找到代码:
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());
}
答案 0 :(得分:29)
不要将ByteArrayOutputStream
传递给PumpStreamHandler
,请使用抽象类org.apache.commons.exec.LogOutputStream
的实现。来自the javadoc:
实现解析传入的数据以构造一条线并将整行传递给用户定义的实现。
因此,LogOutputStram正在预处理输出,以便您控制处理单个行而不是原始字节。像这样:
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;
}
}
然后在阻止拨打exec.execute
之后,您的getLines()
会出现您正在寻找的标准输出和标准错误。从仅执行进程的角度来看,ExecutionResultHandler
是可选的,并将所有stdOut / stdErr收集到一个行列表中。
答案 1 :(得分:3)
我没有得到的是为什么这里不需要'waitfor'样式命令?是否可能有一段时间缓冲区将为空,退出循环,并在进程仍在进行时继续?当我运行它时,情况似乎并非如此。
readLine块。也就是说,您的代码将等到读取一行。
PumpStreamHandler
将标准输出和子过程错误复制到标准输出 和父进程的错误。如果输出或错误流设置为 null,来自该流的任何反馈都将丢失。
答案 2 :(得分:2)
根据James A Wilson的回答,我创建了帮助类&#34; Execute&#34;。它包含了他的答案 为方便起见,还提供了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<String> 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:
<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://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<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;
}
}
答案 3 :(得分:0)
它是一个非常老的线程,但是我不得不使用Apache Commons Exec,并且不得不解决相同的问题。我相信2014年发布的最新版本的Apache Commons Exec,无论有没有看门狗,下面的解决方案都能很好地工作;
class CollectingLogOutputStream implements ExecuteStreamHandler {
private final List<String> lines = new LinkedList<String>();
public void setProcessInputStream(OutputStream outputStream) throws IOException
{
}
//important - read all output line by line to track errors
public void setProcessErrorStream(InputStream inputStream) throws IOException {
InputStreamReader isr = new InputStreamReader(inputStream);
BufferedReader br = new BufferedReader(isr);
String line="";
while( (line = br.readLine()) != null){
//use lines whereever you want - for now just print on console
System.out.println("error:"+line);
}
}
//important - read all output line by line to track process output
public void setProcessOutputStream(InputStream inputStream) throws IOException
{
InputStreamReader isr = new InputStreamReader(inputStream);
BufferedReader br = new BufferedReader(isr);
String line="";
while( (line = br.readLine()) != null){
//use lines whereever you want - for now just print on console
System.out.println("output:"+line);
}
}
public void start() throws IOException {
}
public void stop() throws IOException {
}
}
可以将上述类设置为执行程序的StreamHandler;
//set newly created class stream handler for the executor
executor.setStreamHandler(new CollectingLogOutputStream());
完整代码可在此处获取; https://github.com/raohammad/externalprocessfromjava