跳到正文

命令执行

命令 API 在已连接的 Privileged Server 中启动一个非交互进程。它直接执行参数列表,保持 stdout 和 stderr 分离,不会隐式增加 shell。

启动进程

Privilege.startCommand 挂起到远端进程启动成功,然后返回一次性的 PrivilegeCommandProcess

kotlin
val process = Privilege.startCommand(
    PrivilegeCommand(
        arguments = listOf("/system/bin/id"),
        environment = mapOf("LANG" to "C"),
        workingDirectory = "/data/local/tmp",
    ),
)

第一个参数是可执行文件。需要 shell 解析时显式传入 shell:

kotlin
val process = Privilege.startCommand(
    PrivilegeCommand(
        listOf("/system/bin/sh", "-c", "for i in 1 2 3; do echo \$i; sleep 1; done"),
    ),
)

选择一种消费方式

每个进程只能选择一种输出消费方式。只需要进程退出后的输出时使用 awaitResult()

kotlin
val result = process.use { it.awaitResult() }
println(result.exitCode)
println(result.stdout.toString(Charsets.UTF_8))
println(result.stderr.toString(Charsets.UTF_8))

默认最多分别返回 1 MiB 的 stdout 和 stderr。超出的数据仍会被排空,保证进程能够结束, 同时对应的 stdoutTruncatedstderrTruncated 会变为 true。可以通过正数 maxBytesPerStream 调整限制。

需要实时输出时使用 stream()

kotlin
process.use {
    it.stream().collect { event ->
        when (event) {
            is PrivilegeCommandEvent.Stdout -> showStdout(event.bytes)
            is PrivilegeCommandEvent.Stderr -> showStderr(event.bytes)
            is PrivilegeCommandEvent.Exited -> showExitCode(event.exitCode)
        }
    }
}

Flow 只能收集一次。每条输出流内部保持顺序,但字节块不代表一行文本,也不保证 UTF-8 字符边界;展示文本时应使用增量解码器。退出事件始终位于最后,在两条输出 pipe 都到达 EOF 后发出。非零退出码仍是正常退出事件。

超时和取消

默认超时为 30 秒,从远端进程启动成功后开始计算,新输出不会重置倒计时。慢速命令可以 传入更大的值,传入 null 可禁用截止时间。取消 Flow 收集、取消 awaitResult、调用 cancel()close()、owner 死亡以及服务端关闭都会终止尚未完成的命令。

最多同时运行四个命令,超出后立即失败,不进入等待队列。

明确限制

命令 API 不提供 stdin 或 PTY,不支持交互式 shell、终端尺寸、终端信号、ANSI 渲染或 daemon 管理。部分程序在 stdout 连接 pipe 而非终端时会自行缓存,因此即使传输是流式的, 数据仍可能成批到达。