使用 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,它表明唯一干净的方法是使用 fork
和 dup2
等,但我不清楚它是否或如何翻译到 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 - 根据该文章中引用的“最佳实践”,您有没有机会让它与fork
和 dup2
一起使用?如果不是,为什么选择上述方法?以上是关于使用 Kotlin/Native,我如何执行外部 OS 可执行文件或 $PATH 命令并与其 stdin、stdout 和 stderr 流交互?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 kotlin native 中使用 swift 库?
如何使用 Kotlin/native 生成依赖于另一个的 .framework?
如何在 Kotlin Native 中使用 Http Request 库