从Java运行外部程序,读取输出,允许中断

我想从Java启动一个进程,读取它的输出,并得到它的返回码。 但是,它正在执行,我想能够取消它。 我开始启动这个过程:

ProcessBuilder pb = new ProcessBuilder(args); pb.redirectErrorStream(true); Process proc = pb.start(); 

如果我打电话proc.waitFor(),我不能做任何事情,直到stream程退出。 所以我假设我需要这样的东西:

 while (true) { see if process has exited capture some output from the process decide if I want to cancel it, and if so, cancel it sleep for a while } 

这是正确的吗? 有人可以给我一个如何在Java中做到这一点的例子吗?

以下是我想要做的一个例子:

 ProcessBuilder pb = new ProcessBuilder(args); pb.redirectErrorStream(true); Process proc = pb.start(); InputStream is = proc.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line; int exit = -1; while ((line = br.readLine()) != null) { // Outputs your process execution System.out.println(line); try { exit = proc.exitValue(); if (exit == 0) { // Process finished } } catch (IllegalThreadStateException t) { // The process has not yet finished. // Should we stop it? if (processMustStop()) // processMustStop can return true // after time out, for example. proc.destroy(); } } 

你可以改进它:-)我现在没有真正的环境来testing它,但是你可以在这里find更多的信息。

我build议检查Apache Commons Exec以避免重新创build轮子。 它具有一些很好的function,例如在同步执行和asynchronous执行之间进行select,以及一个产生看门狗进程的标准解决scheme,可以帮助在执行过程中超时,以防卡住。

像这样的助手类可以做到这一点:

 public class ProcessWatcher implements Runnable { private Process p; private volatile boolean finished = false; public ProcessWatcher(Process p) { this.p = p; new Thread(this).start(); } public boolean isFinished() { return finished; } public void run() { try { p.waitFor(); } catch (Exception e) {} finished = true; } } 

然后,您将完全按照您的描述来实现您的循环:

 Process p = Runtime.getRuntime().exec("whatever command"); ProcessWatcher pw = new ProcessWatcher(p); InputStream output = p.getInputStream(); while(!pw.isFinished()) { processOutput(output); if(shouldCancel()) p.destroy(); Thread.sleep(500); } 

取决于什么样的条件会使你想破坏这个过程,你可能想在一个单独的线程中做到这一点。 否则,在等待更多的程序输出进行处理时,可能会阻塞,并且从来没有真正获得销毁它的选项。

编辑 :McDowell是100%正确的在他的评论下面,所以我已经完成variables易变。

这怎么样(看看它如何在jcabi-heroku-maven-plugin中运行 ):

 /** * Wait for the process to stop, logging its output in parallel. * @param process The process to wait for * @return Stdout produced by the process * @throws InterruptedException If interrupted in between */ private String waitFor(final Process process) throws InterruptedException { final BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream()) ); final CountDownLatch done = new CountDownLatch(1); final StringBuffer stdout = new StringBuffer(); new Thread( new VerboseRunnable( new Callable<Void>() { @Override public Void call() throws Exception { while (true) { final String line = reader.readLine(); if (line == null) { break; } System.out.println(">> " + line); stdout.append(line); } done.countDown(); return null; } }, false ) ).start(); try { process.waitFor(); } finally { done.await(); IOUtils.closeQuietly(reader); } return stdout.toString(); } 

PS。 现在这个实现可以从jcabi-log artifact中以com.jcabi.log.VerboseProcess类的forms获得。

什么会让你决定杀死进程 – 一个asynchronous事件(比如来自用户的input)或者一个同步事件(例如,进程已经完成了你想做的事情)? 我猜这是前者 – 来自用户的input让你决定取消subprocess。

另外,你期望子过程产生多less产出? 如果它很多,那么如果你没有足够快的读取输出stream,subprocess可能会被阻塞。

你的情况可能会有所不同,但似乎至less需要两个不同的线程 – 一个决定是否取消进程,另一个处理subprocess的输出。

在这里看看更多的细节: http : //java.sun.com/developer/JDCTechTips/2005/tt0727.html#2