处理 apache-commons exec 的输出
我在这里束手无策。我确信这很简单,而且我对 java 和流的理解很可能有很大的漏洞。我认为类太多了,以至于我有点不知所措,无法通过 API 来弄清楚何时以及如何使用大量的输入/输出流。
我刚刚了解到 apache commons 库的存在(自学 java 失败),目前正在尝试将我的一些 Runtime.getRuntime().exec 转换为使用 commons - exec。它已经修复了一些每 6 个月出现一次这个问题然后消失的 exec 风格问题。
该代码执行 perl 脚本,并在运行时在 GUI 中显示脚本的标准输出。
调用代码位于 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 对象获取输出,并用 perl 脚本的 stdout/err? 中的字节填充我提供给它的 OutputStream?
如果是这样,您将如何获得上述行为以使其逐行流式传输输出?在示例中,人们显示您在最后调用了 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());
}
I am at my wits end here. I'm sure this is something simple and I most likely have huge holes in my understanding of java and streams. I think there are so many classes that I'm a bit overwhelmed with trying to poke through the API to figure out when and how I want to use the multitude of input/output streams.
I just learned about the existence of the apache commons library (self teaching java fail), and am currently trying to convert some of my Runtime.getRuntime().exec to use the commons - exec. Already it's fixed some of the once every 6 months this problem crops up then goes away style problems with exec.
The code executes a perl script, and displays the stdout from the script in the GUI as it is running.
The calling code is inside of a swingworker.
I'm getting lost how to use the pumpStreamHandler... anyway here is the old code:
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();
}
I guess this is what I get for copy pasting code I don't fully understand a long time ago. The above I assume is executing the process, then grabs the outputstream (via "getInputStream"?), places it into a buffered reader, then will just loop there until the buffer is empty.
What I don't get is why there is no need for a 'waitfor' style command here? Isn't it possible that there will be some time in which the buffer will be empty, exit the loop, and continue on while the process is still going? When I run it, this doesn't seem to be the case.
In any event, I'm trying to get the same behavior using commons exec, basically again going from google found code:
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();
I'm trying to figure out what pumpstreamhandler is doing. I assume that this will take the output from the exec object, and fill the OutputStream I provide it with the bytes from the perl script's stdout/err?
If so how would you get the above behavior to have it stream the output line by line? In examples people show you call the out.toString() at the end, and I assume this would just give me a dump of all the output from the script once it is done running? How would you do it such that it would show the output as it is running line by line?
------------Future Edit ---------------------
Found this via google and works nice as well:
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());
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
不要将
ByteArrayOutputStream
传递给PumpStreamHandler
,而是使用抽象类org.apache.commons.exec.LogOutputStream
的实现。来自 javadoc< /a>:因此,LogOutputStram 正在对输出进行预处理,以便您可以控制处理单独的行而不是原始字节。像这样的事情:
然后在阻塞调用
exec.execute
之后,您的getLines()
将具有您正在寻找的标准输出和标准错误。从执行流程并将所有 stdOut/stdErr 收集到行列表的角度来看,ExecutionResultHandler 是可选的。Don't pass a
ByteArrayOutputStream
to thePumpStreamHandler
, use an implementation of the abstract classorg.apache.commons.exec.LogOutputStream
. From the javadoc:Thus the LogOutputStram is preprocessing the output to give you the control of handling individual lines instead of the raw bytes. Something like this:
Then after the blocking call to
exec.execute
yourgetLines()
will have the standard out and standard error you are looking for. TheExecutionResultHandler
is optional from the perspective of just executing the process, and collecting all the stdOut/stdErr into a list of lines.根据 James A Wilson 的回答,我创建了辅助类“执行”。它概括了他的答案
为了方便起见,将其放入还提供 exitValue 的解决方案中。
以这种方式执行命令需要单行:
ExecResult result=Execute.execCmd(cmd,expectedExitCode);
以下 Junit 测试用例测试并显示如何使用它:
Junit4 测试用例:
执行Java帮助类:
Based on James A Wilson's answer I created the helper class "Execute". It wraps his answer
into a solution that also supplies the exitValue for convenience.
A single line is necessary to execute a command this way:
ExecResult result=Execute.execCmd(cmd,expectedExitCode);
The following Junit Testcase tests and shows how to use it:
Junit4 test case:
Execute Java helper Class:
readLine 块。也就是说,您的代码将等待直到读取一行。
PumpStreamHandler
来自 文档
readLine blocks. That is, your code will wait until a line has been read.
PumpStreamHandler
from Documentation
它是一个非常古老的线程,但我必须使用 Apache Commons Exec 并且必须解决同样的问题。我相信 2014 年发布的 Apache Commons Exec 的最新版本,下面的解决方案无论有没有看门狗都可以很好地工作;
上面的类可以设置为执行器的StreamHandler,如下所示;
完整的代码可以在这里找到;
https://github.com/raohammad/externalprocessfromjava
Its a very old thread but I had to use Apache Commons Exec and had to solve the same problem. I trust with last version of Apache Commons Exec published in 2014, below solution works well both with and without watchdog;
Above class can be set as StreamHandler for the executor as below;
Complete code is available here;
https://github.com/raohammad/externalprocessfromjava