Duplicate :
|
|
Relates :
|
|
Relates :
|
|
Relates :
|
Scripting languages like Bourne shell and perl have very simple constructs to run external commands and capture their output. For example, in Bourne shell you can run a command simply like this jar cf foo.jar foo.class and you can capture the output of a command simply like this: output=`jar tf foo.jar` Java has all the infrastructure needed for an application programmer to do the same thing, but it is surprisingly difficult, especially if one wants results to be reliable and never hang. The reason for the difficulty is that the stdout and the stderr of a Java Process is connected to the parent process via operating system pipes. These have fixed size buffers, rather like ArrayBlockingQueue. If the producer of data writes enough data to fill a buffer, it will block until the consumer drains some of the data out of the buffer. If the data is not drained by the consumer, then the producer will block if and only if the data exceeds the size of the buffer. As an example, in the simple shell command cat file | sleep 10000000 whether the cat command will exit quickly depends on the size of the file *and* the system-dependent size of the pipe buffer. The only safe way to capture the output of a command from Java is to start up two threads, one to read the stderr, and one to read the stdout, and then to join these two threads. To force a user to confront a difficult concurrent programming problem when the user is thinking that the problem is serial is very bad. Even an enlightened programmer who knows better might be tempted to write an unreliable expression like new ProcessBuilder(cmd).start().waitFor(); to run a command that is "known" to never create any output, even when this is not completely safe, to save the 50 lines of code required to do this safely. The core libraries should provide a way. One possible way is ProcessResults res = new ProcessBuilder(cmd).run(); String stdout = res.stdout(); String stderr = res.stderr(); Here is some sample code from the regression test for ProcessBuilder: private static class StreamAccumulator extends Thread { private final InputStream is; private final StringBuilder sb = new StringBuilder(); private Throwable throwable = null; public String result () throws Throwable { if (throwable != null) throw throwable; return sb.toString(); } StreamAccumulator (InputStream is) { this.is = is; } public void run() { try { Reader r = new InputStreamReader(is); char[] buf = new char[4096]; int n; while ((n = r.read(buf)) > 0) { sb.append(buf,0,n); } } catch (Throwable t) { throwable = t; } } } private static ProcessResults run(Process p) { Throwable throwable = null; int exitValue = -1; String out = ""; String err = ""; StreamAccumulator outAccumulator = new StreamAccumulator(p.getInputStream()); StreamAccumulator errAccumulator = new StreamAccumulator(p.getErrorStream()); try { outAccumulator.start(); errAccumulator.start(); exitValue = p.waitFor(); outAccumulator.join(); errAccumulator.join(); out = outAccumulator.result(); err = errAccumulator.result(); } catch (Throwable t) { throwable = t; } return new ProcessResults(out, err, exitValue, throwable); } //---------------------------------------------------------------- // Results of a command //---------------------------------------------------------------- private static class ProcessResults { private final String out; private final String err; private final int exitValue; private final Throwable throwable; public ProcessResults(String out, String err, int exitValue, Throwable throwable) { this.out = out; this.err = err; this.exitValue = exitValue; this.throwable = throwable; } public String out() { return out; } public String err() { return err; } public int exitValue() { return exitValue; } public Throwable throwable() { return throwable; } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("<STDOUT>\n" + out() + "</STDOUT>\n") .append("<STDERR>\n" + err() + "</STDERR>\n") .append("exitValue = " + exitValue + "\n"); if (throwable != null) sb.append(throwable.getStackTrace()); return sb.toString(); } } ###@###.### 2004-07-15
|