将进程输出重定向到标准输出

30

我希望能够在Groovy程序中执行foo.bat,并将生成的进程输出重定向到标准输出(stdout)。Java或Groovy代码示例均可。

由于foo.bat可能需要几分钟才能运行并生成大量输出,因此我希望能够在生成输出时立即看到它,而不必等待进程完成后一次性查看所有输出。


将进程输出捕获到线程中的方法 - yegor256
7个回答

69

使用 inheritIO() 方法将所有流重定向到标准输出很简单。这会将输出打印到运行此命令的进程的 stdout。

ProcessBuilder pb = new ProcessBuilder("command", "argument");
pb.directory(new File(<directory from where you want to run the command>));
pb.inheritIO();
Process p = pb.start();
p.waitFor();

还有其他方法,如下所述。这些个别的方法将帮助仅重定向所需的流。

    pb.redirectInput(Redirect.INHERIT)
    pb.redirectOutput(Redirect.INHERIT)
    pb.redirectError(Redirect.INHERIT)

3
这是正确回答问题的答案。OP问怎样重定向输出,而不是如何打印输出的每一行。这个区别非常重要。 - Brandon

37

这里使用了一个类,它可以读取执行程序生成的所有输出,并在自己的标准输出中显示。

class StreamGobbler extends Thread {
    InputStream is;

    // reads everything from is until empty. 
    StreamGobbler(InputStream is) {
        this.is = is;
    }

    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(line);    
        } catch (IOException ioe) {
            ioe.printStackTrace();  
        }
    }
}

Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("javac");
//output both stdout and stderr data from proc to stdout of this process
StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream());
StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream());
errorGobbler.start();
outputGobbler.start();
proc.waitFor();

2
我的问题是,如果此时进程没有输出内容,那么循环会在进程完成之前结束,对吗? - Chris Smith
while循环将等待下一行输入。 - Malcolm Crum

6
如果您希望使用更多的Groovy而不是Java来完成此操作,可以按照以下方式打印每行内容:
def cmd = "./longRunningProcess"

def process = cmd.execute()
process.in.eachLine { line -> println line }

如果您想同时查看标准输出和错误输出,可以使用以下方法:

def cmd = "./longRunningProcess"

def process = cmd.execute()
process.waitForProcessOutput( System.out, System.err )

5
如果你只想获取简单命令的输出,这里有一个更简单的方法。如果你想并行处理或者命令需要输入stdin或生成stderr,则需要像jitter一样使用线程。如果你需要大量输出,请使用缓冲复制(例如像这个)。
import java.io.*;
public class test {
  static void copy(InputStream in, OutputStream out) throws IOException {
    while (true) {
      int c = in.read();
      if (c == -1) break;
      out.write((char)c);
    }
  }

  public static void main(String[] args) throws IOException, InterruptedException {
    String cmd = "echo foo";
    Process p = Runtime.getRuntime().exec(cmd);
    copy(p.getInputStream(), System.out);
    p.waitFor();
  }
}

4
下面的Groovy代码将执行foo.bat并将输出发送到标准输出:
println "foo.bat".execute().text

4
我认为这只会在进程完成后打印所有输出。我想要尽快看到生成的输出。 - Dónal
1
此外,如果 foo.bat 的输出对于 stdout 缓冲区来说“太大”,这也会停止执行 foo.bat。 - billjamesdev

3
异步实现的方法。
void inputStreamToOutputStream(final InputStream inputStream, final OutputStream out) {
    Thread t = new Thread(new Runnable() {

        public void run() {
            try {
                int d;
                while ((d = inputStream.read()) != -1) {
                    out.write(d);
                }
            } catch (IOException ex) {
                //TODO make a callback on exception.
            }
        }
    });
    t.setDaemon(true);
    t.start();
}

{
    Process p = ...;
    inputStreamToOutputStream(p.getErrorStream(), System.out);
    inputStreamToOutputStream(p.getInputStream(), System.out);
}

0

VerboseProcess 可以帮助您从 jcabi-log 中:

String output = new VerboseProcess(new ProcessBuilder("foo.bat")).stdout();

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