调用Runtime.exec时捕获标准输出

当遇到客户端机器上的networking问题时,我希望能够运行一些命令行并将其结果通过电子邮件发送给我自己。

我发现Runtime.exec将允许我执行任意的命令,但收集一个string的结果更有趣。

我意识到我可以将输出redirect到一个文件,然后从文件中读取,但我的spidey感觉告诉我有一个更优雅的方式。

build议?

您需要在进程中捕获std out和std err。 然后你可以写出一个文件/邮件或类似的东西。

有关更多信息,请参见本文 ,并特别注意捕获单独线程中的stdout / err的StreamGobbler机制。 这对于防止阻塞是必不可less的,并且如果您没有正确地做到这一点,它将成为众多错误的根源!

使用ProcessBuilder 。 在调用start()之后,你会得到一个Process对象,从中你可以得到stderr和stdoutstream。

更新:ProcessBuilder给你更多的控制; 您不必使用它,但从长远来看,我发现它更容易。 尤其是将stderrredirect到stdout的能力,这意味着你只需要吸取一个stream。

使用Plexus Utils ,Maven使用它来执行所有外部进程。

 Commandline commandLine = new Commandline(); commandLine.setExecutable(executable.getAbsolutePath()); Collection<String> args = getArguments(); for (String arg : args) { Arg _arg = commandLine.createArg(); _arg.setValue(arg); } WriterStreamConsumer systemOut = new WriterStreamConsumer(console); WriterStreamConsumer systemErr = new WriterStreamConsumer(console); returnCode = CommandLineUtils.executeCommandLine(commandLine, systemOut, systemErr, 10); if (returnCode != 0) { // bad } else { // good } 

对于不会产生太多输出的进程,我认为这个利用Apache IOUtils的简单解决scheme已经足够了:

 Process p = Runtime.getRuntime().exec("script"); p.waitFor(); String output = IOUtils.toString(p.getInputStream()); String errorOutput = IOUtils.toString(p.getErrorStream()); 

警告:但是,如果你的进程产生了很多的输出,这个方法可能会导致问题,正如Process class JavaDoc中提到的那样:

创build的子stream程没有自己的terminal或控制台。 所有标准的io(即stdin,stdout,stderr)操作将通过三个stream(getOutputStream(),getInputStream(),getErrorStream())redirect到父进程。 父进程使用这些stream将input提供给subprocess并从subprocess获取输出。 由于某些本地平台仅为标准input和输出stream提供有限的缓冲区大小,因此如果不及时写入inputstream或读取子stream程的输出stream,可能会导致subprocess阻塞甚至死锁。

Runtime.exec()返回一个Process对象,从中可以提取所运行的任何命令的输出。

jcabi-log中的 VerboseProcess实用程序类可以帮助您:

 String output = new VerboseProcess( new ProcessBuilder("executable with output") ).stdout(); 

唯一的依赖你需要:

 <dependency> <groupId>com.jcabi</groupId> <artifactId>jcabi-log</artifactId> <version>0.7.5</version> </dependency> 

这是我多年来一直使用的帮手类。 一个小class 它有JavaWorld streamgobbler类来修复JVM资源泄漏。 不知道是否对JVM6和JVM7仍然有效,但不会伤害。 助手可以读取输出缓冲区以备后用。

 import java.io.*; /** * Execute external process and optionally read output buffer. */ public class ShellExec { private int exitCode; private boolean readOutput, readError; private StreamGobbler errorGobbler, outputGobbler; public ShellExec() { this(false, false); } public ShellExec(boolean readOutput, boolean readError) { this.readOutput = readOutput; this.readError = readError; } /** * Execute a command. * @param command command ("c:/some/folder/script.bat" or "some/folder/script.sh") * @param workdir working directory or NULL to use command folder * @param wait wait for process to end * @param args 0..n command line arguments * @return process exit code */ public int execute(String command, String workdir, boolean wait, String...args) throws IOException { String[] cmdArr; if (args != null && args.length > 0) { cmdArr = new String[1+args.length]; cmdArr[0] = command; System.arraycopy(args, 0, cmdArr, 1, args.length); } else { cmdArr = new String[] { command }; } ProcessBuilder pb = new ProcessBuilder(cmdArr); File workingDir = (workdir==null ? new File(command).getParentFile() : new File(workdir) ); pb.directory(workingDir); Process process = pb.start(); // Consume streams, older jvm's had a memory leak if streams were not read, // some other jvm+OS combinations may block unless streams are consumed. errorGobbler = new StreamGobbler(process.getErrorStream(), readError); outputGobbler = new StreamGobbler(process.getInputStream(), readOutput); errorGobbler.start(); outputGobbler.start(); exitCode = 0; if (wait) { try { process.waitFor(); exitCode = process.exitValue(); } catch (InterruptedException ex) { } } return exitCode; } public int getExitCode() { return exitCode; } public boolean isOutputCompleted() { return (outputGobbler != null ? outputGobbler.isCompleted() : false); } public boolean isErrorCompleted() { return (errorGobbler != null ? errorGobbler.isCompleted() : false); } public String getOutput() { return (outputGobbler != null ? outputGobbler.getOutput() : null); } public String getError() { return (errorGobbler != null ? errorGobbler.getOutput() : null); } //******************************************** //******************************************** /** * StreamGobbler reads inputstream to "gobble" it. * This is used by Executor class when running * a commandline applications. Gobblers must read/purge * INSTR and ERRSTR process streams. * http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4 */ private class StreamGobbler extends Thread { private InputStream is; private StringBuilder output; private volatile boolean completed; // mark volatile to guarantee a thread safety public StreamGobbler(InputStream is, boolean readStream) { this.is = is; this.output = (readStream ? new StringBuilder(256) : null); } public void run() { completed = false; try { String NL = System.getProperty("line.separator", "\r\n"); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line; while ( (line = br.readLine()) != null) { if (output != null) output.append(line + NL); } } catch (IOException ex) { // ex.printStackTrace(); } completed = true; } /** * Get inputstream buffer or null if stream * was not consumed. * @return */ public String getOutput() { return (output != null ? output.toString() : null); } /** * Is input stream completed. * @return */ public boolean isCompleted() { return completed; } } } 

这是一个从.vbs脚本读取输出的例子,但是对于linux sh脚本也是类似的。

  ShellExec exec = new ShellExec(true, false); exec.execute("cscript.exe", null, true, "//Nologo", "//B", // batch mode, no prompts "//T:320", // timeout seconds "c:/my/script/test1.vbs", // unix path delim works for script.exe "script arg 1", "script arg 2", ); System.out.println(exec.getOutput()); 

使用Runtime.exec给你一个过程。 你可以使用getInputStream获得这个过程的stdout,并通过一个StringBuffer把这个inputstream放到一个String中。