ProcessBuilder挂起[重复]

问题描述 投票:0回答:1

我使用此代码运行命令:

open class AppRunner {

    fun run(
        app: String,
        args: Array<String>,
        timeoutAmount: Long = 6000,
        timeoutUnit: TimeUnit = TimeUnit.SECONDS
    ): AppResult {

        val command = mutableListOf(app)
            .apply {
                addAll(args)
            }

        val commandString = command.joinToString(" ") { "\"$it\"" }
        Kimber.d("Executing command: $commandString")

        val processResult = ProcessBuilder(command)
            .redirectOutput(ProcessBuilder.Redirect.PIPE)
            .redirectError(ProcessBuilder.Redirect.PIPE)
            .start()
            .apply {
                waitFor(timeoutAmount, timeoutUnit)
            }

        val exitCode = processResult.exitValue()
        val stdOut = processResult.inputStream.bufferedReader().readText()
        val stdErr = processResult.errorStream.bufferedReader().readText()

        return AppResult(exitCode, stdOut, stdErr)
    }

    data class AppResult(
        val exitCode: Int,
        val stdOut: String,
        val stdErr: String
    ) {

        fun isSuccessful(): Boolean = exitCode == 0

        fun getStdOutLines(): List<String> = stdOut.split("\n")
        fun getStdErrLines(): List<String> = stdOut.split("\n")

    }

}

像这样:

val args = arrayOf(
                audioFile.absolutePath,
                "-r",
                getRecognizer(language),
                "-f",
                "json",
                "-q"
        )

        val result = appRunner.run(rhubarbBinary.absolutePath, args)

对于某些程序,例如ffmpeg,它可以工作,但上面的示例无效。

«Raw»命令为"/Users/user/<path>/rhubarb" "/var/folders/g6/bmyctvjn7fl3m8kdr0cs1hk80000gn/T/lipsync_audio_14979831388784829375.wav" "-r" "phonetic" "-f" "json" "-q",如果我手动运行它,它将正常工作。

但是如果我使用上面的代码运行它,它将不会启动并冻结。

我确定它没有启动,因为此命令大约需要30秒钟才能完成,并且在运行时会消耗100%的CPU,并且使用此代码运行它根本不会加载CPU。

我在JVM 8,macOS 10.15.4上使用Kotlin 1.3.71。

怎么了?

java kotlin processbuilder
1个回答
1
投票
僵局!

在调用waitFor()之前总是消耗输出。


UPDATE

建议您如下更改代码:

val process = ProcessBuilder(command) .redirectErrorStream(true) .start() val stdOut = processResult.inputStream.bufferedReader().readText() if (process.waitFor(timeoutAmount, timeoutUnit)) { val exitCode = processResult.exitValue() return AppResult(exitCode, stdOut, "") } // timeout: decide what to do here, since command hasn't terminated yet

无需指定Redirect.PIPE,因为这是默认设置。如果您不按如下所示加入stderr和stdout,则需要创建线程来单独使用它们,因为它们都存在缓冲区已满的问题,因此您只能先阅读其中之一。

© www.soinside.com 2019 - 2024. All rights reserved.