为了账号安全,请及时绑定邮箱和手机立即绑定

ProcessBuilder:转发stdout和stderr启动进程而不阻塞主线程

ProcessBuilder:转发stdout和stderr启动进程而不阻塞主线程

一只萌萌小番薯 2019-07-26 16:31:25
ProcessBuilder:转发stdout和stderr启动进程而不阻塞主线程我正在使用ProcessBuilder在Java中构建一个进程,如下所示:ProcessBuilder pb = new ProcessBuilder()         .command("somecommand", "arg1", "arg2")         .redirectErrorStream(true);Process p = pb.start();InputStream stdOut = p.getInputStream();现在我的问题如下:我想捕获通过该进程的stdout和/或stderr的任何内容,并将其重定向到System.out异步。我希望进程及其输出重定向在后台运行。到目前为止,我发现这样做的唯一方法是手动生成一个新的线程,该线程将连续读取stdOut,然后调用适当的write()方法System.out。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();虽然这种方法很有效,但感觉有点脏。最重要的是,它为我提供了一个正确管理和终止的线程。有没有更好的方法来做到这一点?
查看完整描述

3 回答

?
慕的地6264312

TA贡献1817条经验 获得超6个赞

使用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();}

子进程完成后线程将自动死亡,因为srcEOF 会自动死亡。


查看完整回答
反对 回复 2019-07-26
?
HUX布斯

TA贡献1876条经验 获得超6个赞

一个灵活的Java 8 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();}

这里输出流被重定向到,System.out并且错误级别由错误级别记录logger


查看完整回答
反对 回复 2019-07-26
  • 3 回答
  • 0 关注
  • 1234 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信