在某些特定情况下,ProcessBuilder 的输入流没有响应

Getting no response from ProcessBuilder's input stream in some specific case

所以我试图从 linux 获取电池状态,到目前为止,第一个命令(路径变量)returns 非常完美,我能够从输入流,但不幸的是第二个命令(结果变量)returns 空序列。

fun getLinuxBatteryStatus(): Nothing? {
    val path = """upower --enumerate""".runCommand() ?: return null

    val parameters = listOf("present", "state", "energy-full", "energy", "energy-rate", "time to empty", "percentage")
    val result = """upower -i ${path.first { "battery_BAT" in it }} | grep -E "${parameters.joinToString("|")}""""
        .also { println(it) }
        .runCommand() ?: return null

    result.forEach(::println)   // <- no ouput
    // println(result.count())  // <- 0

    /* Do other thing and return something (that is not related to problem) */
}

输出:

upower -i /org/freedesktop/UPower/devices/battery_BAT1 | grep -E "present|state|energy-full|energy|energy-rate|time to empty|percentage"

以上输出来自最后一个命令中的also块,只是为了预览命令的字符串以进行调试。如果我 运行 将上述命令直接输入终端,我将成功获得如下响应:

    present:             yes
    state:               charging
    energy:              47.903 Wh
    energy-empty:        0 Wh
    energy-full:         50.299 Wh
    energy-full-design:  48.004 Wh
    energy-rate:         17.764 W
    percentage:          95%

为什么最后一个命令对 ProcessBuilder 不起作用(不返回任何响应)?

注:扩展函数runCommand取自here

private fun String.runCommand(
    workingDir: File = File("."),
    timeoutAmount: Long = 60,
    timeoutUnit: TimeUnit = TimeUnit.SECONDS
): Sequence<String>? = try {
    ProcessBuilder(split("\s".toRegex()))
        .directory(workingDir)
        .redirectOutput(ProcessBuilder.Redirect.PIPE)
        .redirectError(ProcessBuilder.Redirect.PIPE)
        .start()
        .apply { waitFor(timeoutAmount, timeoutUnit) }
        .inputStream.bufferedReader().lineSequence()
} catch (e: IOException) {
    e.printStackTrace()
    null
}

这里的问题是管道。

您正在尝试 运行 管道 — 涉及 运行 多个程序的构造,需要 shell 来解释。

但是 ProcessBuilder 运行 是一个程序。在这种情况下,它是 运行 程序 upower 并将参数传递给它 -i/org/freedesktop/UPower/devices/battery_BAT1|grep-E,以及 "present|state|energy-full|energy|energy-rate|time to empty|percentage"。显然 upower 不知道如何处理 | 参数或其后的参数。

您可以使用 ProcessBuilder 来 运行 创建一个 shell 实例,然后它可以 运行 您的管道;参见 this answer

但在您自己的代码中进行过滤并避免完全调用 grep 可能会更简单、更安全、更高效。

我建议捕获进程的错误输出,这很可能会使问题变得清晰。