Java 调用 Runtime.exec 时捕获标准输出
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/882772/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Capturing stdout when calling Runtime.exec
提问by Allain Lalonde
When experiencing networking problems on client machines, I'd like to be able to run a few command lines and email the results of them to myself.
在客户端机器上遇到网络问题时,我希望能够运行一些命令行并将结果通过电子邮件发送给我自己。
I've found Runtime.exec will allow me to execute arbitrary commands, but Collecting the results in a String is more interesting.
我发现 Runtime.exec 将允许我执行任意命令,但收集字符串中的结果更有趣。
I realize I could redirect output to a file, and then read from the file, but my spidey sense is telling me there's a more elegant way of doing it.
我意识到我可以将输出重定向到一个文件,然后从文件中读取,但我的诡异感告诉我有一种更优雅的方式来做到这一点。
Suggestions?
建议?
采纳答案by Brian Agnew
You need to capture both the std out and std err in the process. You can then write std out to a file/mail or similar.
您需要在此过程中同时捕获 std out 和 std err。然后,您可以将 std 写入文件/邮件或类似文件。
See this articlefor more info, and in particular note the StreamGobbler
mechanism that captures stdout/err in separate threads. This is essential to prevent blocking and is the source of numerous errors if you don't do it properly!
有关更多信息,请参阅此文章,并特别注意StreamGobbler
在单独的线程中捕获 stdout/err的机制。这对于防止阻塞至关重要,如果操作不当,则会导致许多错误!
回答by basszero
Use ProcessBuilder. After calling start() you'll get a Processobject from which you can get the stderr and stdout streams.
使用ProcessBuilder。调用 start() 后,您将获得一个Process对象,您可以从中获取 stderr 和 stdout 流。
UPDATE: ProcessBuilder gives you more control; You don't have to use it but I find it easier in the long run. Especially the ability to redirect stderr to stdout which means you only have to suck down one stream.
更新:ProcessBuilder 为您提供更多控制权;你不必使用它,但我发现从长远来看它更容易。尤其是将 stderr 重定向到 stdout 的能力,这意味着您只需吸收一个流。
回答by PaulJWilliams
Runtime.exec() returns a Process object, from which you can extract the output of whatever command you ran.
Runtime.exec() 返回一个 Process 对象,您可以从中提取您运行的任何命令的输出。
回答by Valentin Rocher
Using Runtime.exec gives you a process. You can these use getInputStreamto get the stdout of this process, and put this input stream into a String, through a StringBuffer for example.
使用 Runtime.exec 为您提供了一个过程。您可以使用getInputStream来获取此过程的标准输出,并将此输入流放入字符串中,例如通过 StringBuffer。
回答by adrian.tarau
Use Plexus Utils, it is used by Maven to execut all external processes.
使用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
}
回答by yegor256
VerboseProcess
utility class from jcabi-logcan help you:
VerboseProcess
jcabi-log 中的实用程序类可以帮助您:
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>
回答by Whome
This is my helper class been using for years. One small class. It has JavaWorld streamgobbler class to fix JVM resource leaks. Don't know if still valid for JVM6 and JVM7 but does not hurt. Helper can read output buffer for later use.
这是我多年来一直使用的助手类。一个小班。它有 JavaWorld streamgobbler 类来修复 JVM 资源泄漏。不知道对 JVM6 和 JVM7 是否仍然有效,但没有伤害。Helper 可以读取输出缓冲区以备后用。
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;
}
}
}
Here is an example reading output from .vbs script but similar works for linux sh scripts.
这是从 .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());
回答by erwaman
For processes that don't generate much output, I think this simple solution that utilizes Apache IOUtilsis sufficient:
对于不产生太多输出的进程,我认为这个利用Apache IOUtils 的简单解决方案就足够了:
Process p = Runtime.getRuntime().exec("script");
p.waitFor();
String output = IOUtils.toString(p.getInputStream());
String errorOutput = IOUtils.toString(p.getErrorStream());
Caveat: However, if your process generates a lot of output, this approach may cause problems, as mentioned in the Process class JavaDoc:
警告:但是,如果您的流程生成大量输出,则此方法可能会导致问题,如Process 类 JavaDoc 中所述:
The created subprocess does not have its own terminal or console. All its standard io (i.e. stdin, stdout, stderr) operations will be redirected to the parent process through three streams (getOutputStream(), getInputStream(), getErrorStream()). The parent process uses these streams to feed input to and get output from the subprocess. Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock.
创建的子进程没有自己的终端或控制台。它的所有标准io(即stdin、stdout、stderr)操作将通过三个流(getOutputStream()、getInputStream()、getErrorStream())重定向到父进程。父进程使用这些流向子进程提供输入和从子进程获取输出。由于一些原生平台只为标准输入输出流提供有限的缓冲区大小,如果不能及时写入输入流或读取子进程的输出流,可能会导致子进程阻塞,甚至死锁。