【问题标题】:listen for Runtime.exec() events监听 Runtime.exec() 事件
【发布时间】:2020-05-01 14:27:49
【问题描述】:

我有一个函数,它使用以下代码将字符串作为命令执行:

fun String.runAsCommand() : String {
    val process = Runtime.getRuntime().exec(this)
    if (!process.waitFor(60, TimeUnit.SECONDS)) {
        process.destroy()
        throw RuntimeException("execution timed out: $this")
    }
    if (process.exitValue() != 0) {
        throw RuntimeException("execution failed with code ${process.exitValue()}: $this")
    }

    return process.inputStream.bufferedReader().readText()
}

但是,一旦一切完成,此代码就会输出命令输出。但有问题的过程实际上是一个漫长的过程,最多需要 40 秒,并逐渐将状态输出到控制台。如何通过某种侦听器结构拦截这些回声/控制台日志?

【问题讨论】:

    标签: java kotlin process


    【解决方案1】:

    标准方法是使用ProcessBuilder,并启动另一个线程来读取进程的标准输出(这是您的输入)。

    我不确定这是否是最好的方法,但这里有一些我想出的代码来做一些非常相似的事情。 (在我的例子中,进程写入了它的 stdout 和 stderr,所以我需要同时阅读它们——但在进程完成之前我不需要查看它们。我还需要返回进程的退出状态以及这两个状态, 并处理超时。不过,我不需要向进程的标准输入发送任何内容;如果需要,则必须对其进行扩展。)

    /**
    * Runs a system command and captures its output (and stderr).
    *
    * @param command The program name and any arguments.
    * @param workingDir The working directory of the process, or `null` for the same as this process.
    * @param timeoutSecs Maximum time to wait for it to finish, in seconds.  (Default is 5 mins.)
    * @param throwOnFailure Whether to throw a [ProcessFailedException] if it returns a non-zero exit value.
    * @throws IndexOutOfBoundsException if the command is empty.
    * @throws SecurityException if a security manager prevented creation of the process or a redirection.
    * @throws UnsupportedOperationException if the OS doesn't support process creation.
    * @throws IOException if an I/O error occurs.
    * @throws ProcessTimedOutException if the timeout expires before the process finishes.
    * @throws ProcessFailedException if the process returns a non-zero exit status.
    */
    fun runProcess(vararg command: String, workingDir: File? = null, timeoutSecs: Long = 300,
                    throwOnFailure: Boolean = true): ProcessResult {
        val proc = ProcessBuilder(*command)
                    .directory(workingDir)
                    .start()
    
        // Must read both output and error, else it can deadlock.
    
        class StreamReader(val stream: InputStream, val result: StringBuilder) : Runnable {
            override fun run() = stream.bufferedReader().use { s ->
                while (true)
                    result.appendln(s.readLine() ?: break)
            }
        }
    
        val output = StringBuilder()
        Thread(StreamReader(proc.inputStream, output)).start()
        val error = StringBuilder()
        Thread(StreamReader(proc.errorStream, error)).start()
    
        val exited = proc.waitFor(timeoutSecs, TimeUnit.SECONDS)
    
        if (!exited)
            throw ProcessTimedOutException("${command[0]} timed out!", timeoutSecs, output.toString(), error.toString())
        val exitValue = proc.exitValue()
        if (exitValue != 0 && throwOnFailure)
            throw ProcessFailedException("${command[0]} failed: $error", exitValue, output.toString(), error.toString())
        return ProcessResult(exitValue, output.toString(), error.toString())
    }
    
    /** Thrown if a process doesn't finish within the timeout period. */
    class ProcessTimedOutException(msg: String, val timeoutSecs: Long, val output: String, val error: String) : Exception(msg)
    
    /** Thrown if a process returns a non-zero code. */
    class ProcessFailedException(msg: String, val exitValue: Int, val output: String, val error: String) : Exception(msg)
    
    /** Returned if a process completes. */
    class ProcessResult(val exitValue: Int, val output: String, val error: String)
    

    【讨论】:

    • 这个不行,完成后还是会输出这个过程的结果
    • 正如我所说,我的代码正在做一些类似的事情——可能不是你想要的。 (虽然你没有提供任何细节,所以很难确定。)但这应该说明总体思路;也许你可以看到你可以在那里调整 StreamReader。
    • 在做了一些挖掘之后,我认为它的实际命令有问题。这是一个调用 printf() 而不是 println() 的 C++ 程序 - 我认为
    • 调用printf() 本身并不一定是个问题——过去总是这样。但是stdout 通常是行缓冲的,所以如果这些调用不包括\n 行分隔符,那么它可能不会是flush() 流,并且部分或全部输出可能会卡在一个该进程中的缓冲区,但尚未对您的 Kotlin 程序可用。 (您可以尝试将其输出重定向到通常没有缓冲的 stderr,但我不知道这是否可行。)
    • 嗯,这很奇怪,因为 printf() 确实 在语句 github.com/prusa3d/PrusaSlicer/blob/master/src/libslic3r/… 的末尾包含了一个 \n
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 2015-06-23
    • 2020-06-25
    • 2011-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多