Java进程获取输出并设置超时

Posted

技术标签:

【中文标题】Java进程获取输出并设置超时【英文标题】:Java Process get output and set timeout 【发布时间】:2017-05-20 08:49:36 【问题描述】:

如何在设置超时值时获取进程的输出?

我目前正在使用 apache commons io utils 从进程的标准和错误输出中创建一个字符串。

下面的代码(与 cmets 一样)适用于终止的进程。但是,如果进程没有终止,主线程也不会终止!

如果我取消注释掉注释代码,而是注释掉 process.waitfor(),该方法将正确地销毁非终止进程。但是,对于终止进程,无法正确获得输出。看来waitfor() 完成后,我无法获取进程的输入和错误流?

最后,如果我尝试将注释部分移动到 process.waitfor() 当前所在的位置,删除 process.waitfor() 并取消注释注释部分,那么对于非终止进程,主线程也不会停止。这是因为 process.waitfor(15, ...) 永远不会到达。

private static Outputs runProcess(String command) throws Exception 
    Process process = Runtime.getRuntime().exec(command);

    // if (!process.waitFor(15, TimeUnit.SECONDS)) 
    // System.out.println("Destroy");
    // process.destroy();
    // 

    // Run and collect the results from the standard output and error output
    String stdStr = IOUtils.toString(process.getInputStream());
    String errStr = IOUtils.toString(process.getErrorStream());

    process.waitFor();

    return new Outputs(stdStr, errStr);

【问题讨论】:

您需要在单独的线程中获取输出。 【参考方案1】:

正如@EJP 建议的那样,您可以使用不同的线程来捕获流或使用ProcessBuilder 或从您的命令重定向到文件。 以下是我认为您可以使用的 3 种方法。

    为 Streams 使用不同的线程。

    Process process = Runtime.getRuntime().exec("cat ");
    
    ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(2);
    
    Future<String> output = newFixedThreadPool.submit(() -> 
        return IOUtils.toString(process.getInputStream());
    );
    Future<String> error = newFixedThreadPool.submit(() -> 
        return IOUtils.toString(process.getErrorStream());
    );
    
    newFixedThreadPool.shutdown();
    
    // process.waitFor();
    if (!process.waitFor(3, TimeUnit.SECONDS)) 
        System.out.println("Destroy");
        process.destroy();
    
    
    System.out.println(output.get());
    System.out.println(error.get());
    

    使用 ProcessBuilder

    ProcessBuilder processBuilder = new ProcessBuilder("cat")
            .redirectError(new File("error"))
            .redirectOutput(new File("output"));
    
    Process process = processBuilder.start();
    
    // process.waitFor();
    if (!process.waitFor(3, TimeUnit.SECONDS)) 
        System.out.println("Destroy");
        process.destroy();
    
    
    System.out.println(FileUtils.readFileToString(new File("output")));
    System.out.println(FileUtils.readFileToString(new File("error")));
    

    在命令中使用重定向运算符将输出和错误重定向到文件,然后从文件中读取。

Here 是一个非常好的博客,它解释了处理Runtime.Exec 的不同方法

【讨论】:

在你的第一个例子中。您使用 threadPool.shutdown()。 javadoc 有点混乱。 启动有序关闭,其中执行先前提交的任务,但不会接受新任务 vs 此方法不等待先前提交的任务完成执行。使用 awaitTermination 来做到这一点。 那么这会提前终止线程吗? @Irxw Shutdown 只会告诉执行者停止接受新请求。但是,现有/提交的任务将继续运行而不会中断。此外,呼叫Shutdown 不会阻止您等待所有提交的任务完成的呼叫。为确保您希望等待所有任务完成,请调用Shutdown,然后调用AwaitTermination。有 ShutdownNow 尽最大努力通过主要中断等待/运行的线程来做到这一点,但即使这样也不能保证提交的任务将被杀死/中断。 谢谢,我将“不等待”理解为“它会杀死它”。但它只是说它没有阻塞并等待它完成。但它仍然优雅。

以上是关于Java进程获取输出并设置超时的主要内容,如果未能解决你的问题,请参考以下文章

[Java]_[初级]_[如何调用外部命令获取输出并设置它的超时退出]

python:运行一个超时的进程并捕获stdout、stderr和退出状态[重复]

使用输出和超时启动python进程

用tcpclient的异步连接方式,设置连接超时

在 C++ 中使用管道和超时执行命令(并读取标准输出)

如何在 Java 中为进程实现超时?