使用 Kotlin/Native,我如何执行外部 OS 可执行文件或 $PATH 命令并与其 stdin、stdout 和 stderr 流交互?

Posted

技术标签:

【中文标题】使用 Kotlin/Native,我如何执行外部 OS 可执行文件或 $PATH 命令并与其 stdin、stdout 和 stderr 流交互?【英文标题】:Using Kotlin/Native, how can I execute an external OS executable or $PATH command and interact with its stdin, stdout, and stderr streams? 【发布时间】:2019-12-03 00:56:11 【问题描述】:

我正在为用 Kotlin/Native 编写的命令行工具进行概念验证,因为它似乎是跨平台二进制文件的理想语言和运行时。

此命令行工具需要定期与 os 用户 $PATH 或 shell 环境中的操作系统可执行文件、命令和/或 shell 函数交互。但是,我在 kotlin-native samples 或任何地方的文档中没有看到任何可能表明如何:

执行操作系统可执行文件或 $PATH 命令 获取执行的返回码(整数) 确保执行进程的stdin、stdout和stderr文件描述符流可以分别表示为OutputStream和InputStreams

在 JVM 领域,我们会使用 java.lang.ProcessBuilder 来完成所有这些操作,但这显然在 Kotlin/Native 中不可用。

我找到了 cinterop/posix platform.posix.system 函数,但它不允许您访问进程的流。

在我的网络研究中,我发现了一个非常好的 C tutorial,它表明唯一干净的方法是使用 forkdup2 等,但我不清楚它是否或如何翻译到 Kotlin/Native 代码。

【问题讨论】:

您好! AFAIK,fork()dup2 都必须在 platform.posix.* 中,与编译器一起分发。你确定这还不够吗? @ArtyomDegtyarev 我知道它们在那里,但我从未使用过它们。因此,对我来说,如何 1) 以 C+Kotlin 最佳实践的方式通过适当的错误处理有效地使用它们并 2) 包装它们以启用类似于 ProcessBuilder 的东西,这对我来说并不明显。 有这方面的消息吗?同时可以用 kotlin native 执行 Process 吗?? @DirkHoffmann 不幸的是没有。我没有时间自己实现它,而且我知道还没有原生解决方案。 【参考方案1】:

我对 kotlin native 进行了一些尝试,并成功为 jvm、mac、unix 和(未经测试的)windows 执行了 posix exec 命令...

https://github.com/hoffipublic/minimal_kotlin_multiplatform

目标共同

fun String.executeCommand(
    redirectStderr: Boolean = true
): String? = MppProcess.executeCommand(this, redirectStderr)

interface IMppProcess 
    fun executeCommand(
        command: String,
        redirectStderr: Boolean = true
    ): String?


expect object MppProcess : IMppProcess 
    override fun executeCommand(
        command: String,
        redirectStderr: Boolean
    ): String?

目标jvm

actual object MppProcess : IMppProcess 
    actual override fun executeCommand(
        command: String,
        redirectStderr: Boolean
    ): String? 
        return runCatching 
            ProcessBuilder(command.split(Regex("(?<!(\"|').0,255) | (?!.*\\1.*)")))
                //.directory(workingDir)
                .redirectOutput(ProcessBuilder.Redirect.PIPE)
                .apply  if (redirectStderr) this.redirectError(ProcessBuilder.Redirect.PIPE) 
                .start().apply  waitFor(60L, TimeUnit.SECONDS) 
                .inputStream.bufferedReader().readText()
        .onFailure  it.printStackTrace() .getOrNull()
    

目标 mac/unix/windows

import kotlinx.cinterop.refTo
import kotlinx.cinterop.toKString
import platform.posix.fgets
import platform.posix.pclose
import platform.posix.popen

actual object MppProcess : IMppProcess 
    actual override fun executeCommand(
        command: String,
        redirectStderr: Boolean
    ): String? 
        val commandToExecute = if (redirectStderr) "$command 2>&1" else command
        val fp = popen(commandToExecute, "r") ?: error("Failed to run command: $command")

        val stdout = buildString 
            val buffer = ByteArray(4096)
            while (true) 
                val input = fgets(buffer.refTo(0), buffer.size, fp) ?: break
                append(input.toKString())
            
        

        val status = pclose(fp)
        if (status != 0) 
            error("Command `$command` failed with status $status$if (redirectStderr) ": $stdout" else """)
        

        return stdout
    

现在只执行了 ls,但它可以工作。

【讨论】:

Dirk - 根据该文章中引用的“最佳实践”,您有没有机会让它与 forkdup2 一起使用?如果不是,为什么选择上述方法?

以上是关于使用 Kotlin/Native,我如何执行外部 OS 可执行文件或 $PATH 命令并与其 stdin、stdout 和 stderr 流交互?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 kotlin native 中使用 swift 库?

如何使用 Kotlin/native 生成​​依赖于另一个的 .framework?

如何在 Kotlin Native 中使用 Http Request 库

如何避免“附加到使用 kotlin/native 编译器编译的项目的库”错误?

Kotlin/Native 垃圾收集器如何在 C 中工作?

Kotlin/Native IDE支持预览