我正在使用ProcessBuilder在Java中构建一个进程,如下所示:
ProcessBuilder pb = new ProcessBuilder()
.command("somecommand", "arg1", "arg2")
.redirectErrorStream(true);
Process p = pb.start();
InputStream stdOut = p.getInputStream();现在我的问题如下:我希望捕获该进程的标准输出和/或标准错误,并将其异步重定向到System.out。我希望进程及其输出重定向在后台运行。到目前为止,我找到的唯一方法是手动生成一个新线程,该线程将不断地从stdOut读取数据,然后调用System.out的适当write()方法。
new Thread(new Runnable(){
public void run(){
byte[] buffer = new byte[8192];
int len = -1;
while((len = stdOut.read(buffer)) > 0){
System.out.write(buffer, 0, len);
}
}
}).start();虽然这种方法行得通,但感觉有点脏。最重要的是,它给了我另一个线程来管理和正确终止。有没有更好的方法来做到这一点?
发布于 2013-01-05 05:50:57
对于Java7和更高版本的,请参阅Evgeniy Dorofeev的answer。
对于Java6和更早版本的,请创建并使用StreamGobbler
StreamGobbler errorGobbler =
new StreamGobbler(p.getErrorStream(), "ERROR");
// any output?
StreamGobbler outputGobbler =
new StreamGobbler(p.getInputStream(), "OUTPUT");
// start gobblers
outputGobbler.start();
errorGobbler.start();..。
private class StreamGobbler extends Thread {
InputStream is;
String type;
private StreamGobbler(InputStream is, String type) {
this.is = is;
this.type = type;
}
@Override
public void run() {
try {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null)
System.out.println(type + "> " + line);
}
catch (IOException ioe) {
ioe.printStackTrace();
}
}
}发布于 2013-01-05 10:54:23
使用ProcessBuilder.inheritIO,它将子进程标准I/O的源和目标设置为与当前Java进程的源和目标相同。
Process p = new ProcessBuilder().inheritIO().command("command1").start();如果Java 7不是一个选项
public static void main(String[] args) throws Exception {
Process p = Runtime.getRuntime().exec("cmd /c dir");
inheritIO(p.getInputStream(), System.out);
inheritIO(p.getErrorStream(), System.err);
}
private static void inheritIO(final InputStream src, final PrintStream dest) {
new Thread(new Runnable() {
public void run() {
Scanner sc = new Scanner(src);
while (sc.hasNextLine()) {
dest.println(sc.nextLine());
}
}
}).start();
}线程将在子进程结束时自动终止,因为src将EOF。
发布于 2015-10-28 16:59:07
使用Java8 lambda的灵活解决方案,允许您提供一个处理输出的Consumer (例如,记录)逐行记录。run()是一个没有抛出检查异常的一行程序。作为实现Runnable的替代方案,它也可以像其他答案所建议的那样扩展Thread。
class StreamGobbler implements Runnable {
private InputStream inputStream;
private Consumer<String> consumeInputLine;
public StreamGobbler(InputStream inputStream, Consumer<String> consumeInputLine) {
this.inputStream = inputStream;
this.consumeInputLine = consumeInputLine;
}
public void run() {
new BufferedReader(new InputStreamReader(inputStream)).lines().forEach(consumeInputLine);
}
}然后,您可以像这样使用它:
public void runProcessWithGobblers() throws IOException, InterruptedException {
Process p = new ProcessBuilder("...").start();
Logger logger = LoggerFactory.getLogger(getClass());
StreamGobbler outputGobbler = new StreamGobbler(p.getInputStream(), System.out::println);
StreamGobbler errorGobbler = new StreamGobbler(p.getErrorStream(), logger::error);
new Thread(outputGobbler).start();
new Thread(errorGobbler).start();
p.waitFor();
}在这里,logger将输出流重定向到System.out,并在错误级别记录错误流。
https://stackoverflow.com/questions/14165517
复制相似问题