Running stdout when calling Runtime.exec

If there are network problems on client computers, I would like to be able to run several command lines and send the results by e-mail to myself.

I found Runtime.exec will allow me to execute arbitrary commands, but collecting results in String is more interesting.

I understand that I can redirect the output to a file and then read from the file, but my common sense tells me that there is a more elegant way to do this.

Suggestions?

+48
java shell runtime
May 19 '09 at 1:31 pm
source share
8 answers

You need to write both std out and std err in the process. Then you can write std to file / mail or the like.

For more details see this article and, in particular, pay attention to the StreamGobbler mechanism, which captures stdout / err in separate streams. This is necessary to prevent blocking and is the source of numerous errors if you do not do it right!

+44
May 19 '09 at 1:35 pm
source share

Use ProcessBuilder . After calling start (), you get a Process object from which you can get the stderr and stdout threads.

UPDATE: ProcessBuilder gives you more control; You don’t have to use it, but it’s easier for me in the long run. In particular, the ability to redirect stderr to stdout, which means that you only need to disconnect a single thread.

+13
May 19 '09 at 1:33 pm
source share

Use Plexus Utils , it is used by Maven to execute all external processes.

 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 } 
+5
May 19 '09 at 1:42
source share

For processes that don't generate a lot of output, I think this is a simple solution that uses Apache IOUtils :

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

Warning. However, if your process generates a lot of output, this approach can cause problems, as stated in the Process class JavaDoc :

The created subprocess does not have its own terminal or console. All its standard io operations (i.e., Stdin, stdout, stderr) will be redirected to the parent process through three streams (getOutputStream (), getInputStream (), getErrorStream ()). The parent process uses these threads to feed input and receive output from the subprocess. Since some proprietary platforms provide a limited buffer size for standard input and output streams, the inability to quickly write the input stream or read the output stream of a subprocess can lead to blocking of the subprocess and even deadlock.

+4
Jun 24 '14 at 22:10
source share

Runtime.exec () returns a Process object from which you can extract the result of any command that you run.

+2
May 19 '09 at 1:33 pm
source share

VerboseProcess utility class from jcabi-log can help you:

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

The only dependency you need:

 <dependency> <groupId>com.jcabi</groupId> <artifactId>jcabi-log</artifactId> <version>0.7.5</version> </dependency> 
+2
Jan 02 '13 at 15:45
source share

This is my helper class, which has been used for many years. One small class. It has a JavaWorld class streamgobbler for fixing JVM resource leaks. I don’t know if it is still valid for JVM6 and JVM7, but it will not hurt. The assistant can read the output buffer for later use.

 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 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; } } } 

Here is an example of reading the output from a .vbs script, but a similar one works for linux sh scripts.

  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()); 
+2
Sep 23 '13 at 9:08 on
source share

Using Runtime.exec provides you with a process. You can use getInputStream to get the standard output of this process, and, for example, put this input stream into a String through a StringBuffer.

+1
May 19 '09 at 1:37 p.m.
source share



All Articles