将Runtime exec()的输出流打印到控制台

51
我正在尝试获取由exec()启动的ProcessOutputStream以便输出到控制台。如何实现?
以下是一些不完整的代码:
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Reader;

public class RuntimeTests
{
    public static void main(String[] args)
    {
        File path = new File("C:\\Dir\\Dir2");
        String command = "cmd /c dir";
        Reader rdr = null;
        PrintStream prtStrm = System.out;
        try
        {
            Runtime terminal = Runtime.getRuntime();

            OutputStream rtm = terminal.exec(command, null, path).getOutputStream();
            prtStrm = new PrintStream(rtm);
            prtStrm.println();
        } 
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}
7个回答

118

我最近遇到了这个问题,想提一下自从Java 7以来进程构建器API已经扩展了。现在可以使用以下代码解决此问题:

ProcessBuilder pb = new ProcessBuilder("yourcommand");
pb.redirectOutput(Redirect.INHERIT);
pb.redirectError(Redirect.INHERIT);
Process p = pb.start();

3
完美而优雅的回答。如果您可以使用Java 7,那绝对是最佳选择。 - Shane
1
这是最好的答案,即使上面有好的答案,也要尽量使用Java默认提供的工具箱中的工具。Java 7/8是一种极其能干和强大的语言 - 具有不断扩展但高效的工具集。 - DtechNet
我在使用Java 11时遇到了以下错误信息:"Corrupted STDOUT by directly writing to native stream in forked JVM 1." 这个解决方案可能在Java 11中不再适用吗? - Erik Nellessen
谢谢!pb.redirectOutput(Redirect.INHERIT);解决了很多其他在线示例缺少的奇怪问题。 - BitfulByte
@BitfulByte 很高兴能够帮助,这实际上是我在 TipRanks 的第一个编码任务! - Benjamin Gruenbaum

46

我认为这就是你要找的内容:

  String line;
  Process p = Runtime.getRuntime().exec(...);
  BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
  while ((line = input.readLine()) != null) {
    System.out.println(line);
  }
  input.close();

3
我有一个问题与你的解决方案有关:是否不需要使用process.waitFor()函数?它似乎没有使用也可以正常工作,但是为什么?InputStreamReader会等待流结束吗? - das Keks
2
是的,API:如果因为已经到达流的末尾而没有可用字节,则返回值-1。此方法会阻塞,直到有输入数据可用、检测到流的末尾或抛出异常。 - Stijn Geukens
为了完整性,请添加一个 redirectErrorStream(true) - Jeffrey Blattman
为什么要使用inputStream?我们关心的是输出,不是吗? - Gavriel

13

我遇到了类似的问题,我正在使用以下代码。

Process p = Runtime.getRuntime().exec(".....");
p.waitFor();

String line;

BufferedReader error = new BufferedReader(new InputStreamReader(p.getErrorStream()));
while((line = error.readLine()) != null){
    System.out.println(line);
}
error.close();

BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
while((line=input.readLine()) != null){
    System.out.println(line);
}

input.close();

OutputStream outputStream = p.getOutputStream();
PrintStream printStream = new PrintStream(outputStream);
printStream.println();
printStream.flush();
printStream.close();

9
请参考 Benjamin Gruenbaum 在 Java 7 中提供的 ProcessBuilder API。需要启动一个新线程来读取 Process.waitFor() 后发生的进程输出。然后可以从该线程将进程输出复制到控制台。Process.getOutputStream() 实际上是输入,即进程的 stdin。Process.getInputStream() 和 Process.getErrorStream() 返回的是 InputStream 对象,它们是进程的 stdout 和 stderr。InputStream 和 OutputStream 是从调用者的角度来看的,这与进程的角度相反。进程的输出 stdout 是调用者的输入,因此调用者获取一个 getInputStream() 并从中读取 stdout。
Process proc = Runtime.getRuntime().exec(cmdArr, env, cwdir);

InputStreamReader isr = new InputStreamReader(proc.getInputStream());
BufferedReader rdr = new BufferedReader(isr);
while((line = rdr.readLine()) != null) { 
  System.out.println(line);
} 

isr = new InputStreamReader(proc.getErrorStream());
rdr = new BufferedReader(isr);
while((line = rdr.readLine()) != null) { 
  System.out.println(line);
} 
rc = proc.waitFor();  // Wait for the process to complete

Process.getOutputStream()用于调用者向进程的标准输入(stdin)“输出”数据。在上面示例的第一行创建'proc'之后,可以添加此代码将数据发送到进程的标准输入(stdin):

// Send data to stdin of the process (in development, needs input content)
OutputStream os = proc.getOutputStream();
Bytes content = new Bytes("Hello\nasdf\n adma");
os.write(content.arr, content.off, content.length());
os.close(); // should use try/finally or try/resource

在阅读输出之前,请完成上述操作。记得关闭 OutputStream os(可以使用 try/resource 或者 try/finally)。这样,进程就知道 stdin 已经完成,并且可以结束处理信息。

17
现在来到这个问题的所有人:向下滚动,下面有一个更好的答案。 - forresthopkinsa
1
(回答已经被编辑为更好的回答。) - Ribo

5

我知道这是一个非常老的问题,但以上答案的更好替代方案是

ProcessBuilder builder = new ProcessBuilder(command);
builder.inheritIO();
Process p = builder.start();
ProcessBuilder.inheritIO()的文档说明如下:

将子进程标准输入/输出的源和目的地设置为与当前 Java 进程相同。

希望对大家有所帮助!

2

如果您可以使用commons-io中的org.apache.commons.io.IOUTils:

System.out.println(IOUtils.toString(process.getInputStream()));
System.err.println(IOUtils.toString(process.getErrorStream()));

2
尝试使用 VerboseProcessjcabi-log 中获取:
String output = new VerboseProcess(new ProcessBuilder("foo.bat")).stdout();

这句话的意思是:该类启动一个后台线程,监听输出流,并记录日志。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接