Kotlin本机执行命令并获取输出

MG *_*ine 1 terminal posix kotlin-native

我想知道kotlin本机中是否存在一种通过posix调用命令并接收其终端输出的方法。例如,我想使“ git diff”命令正常工作,而不必创建一个临时文件,将输出写入该文件,然后从该文件中读取。

在SO上,我仅找到需要ProcessBuilder的解决方案,由于它是Java库,因此在kotlin-native上不可用。

MG *_*ine 5

我找到了想要使用的一段代码,因此将其发布在这里,供将来的观众使用!

fun executeCommand(command: String): String{
    val fp: CPointer<FILE>? = popen(command, "r")
    val buffer = ByteArray(4096)
    val returnString = StringBuilder()

    /* Open the command for reading. */
    if (fp == NULL) {
        printf("Failed to run command\n" )
        exit(1)
    }

    /* Read the output a line at a time - output it. */
    var scan = fgets(buffer.refTo(0), buffer.size, fp)
    if(scan != null) {
        while (scan != NULL) {
            returnString.append(scan!!.toKString())
            scan = fgets(buffer.refTo(0), buffer.size, fp)
        }
    }
    /* close */
    pclose(fp)
    return returnString.trim().toString()
}
Run Code Online (Sandbox Code Playgroud)


gil*_*dor 5

It's an improved version of exec command for Kotlin Native posted by mg-lolenstine, it throws an exception with command stderr instead of just returning exit(1) (which not always desirable behavior itself), also trim is now optional

import kotlinx.cinterop.*
import platform.posix.*

fun executeCommand(
    command: String,
    trim: Boolean = true,
    redirectStderr: Boolean = true
): 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 if (trim) stdout.trim() else stdout
}
Run Code Online (Sandbox Code Playgroud)


bpp*_*man 5

通常您可以使用api并POSIX使用一些 I/O 相关函数来达到您的目的forkwait

fun main() {
    val childPid: pid_t = fork()
    if (childPid == 0) {
        val commands = listOf("git", "diff", "HEAD^1", "$projectDir/path/to/file", null)
        val cwd = "$projectDir"
        chdir(cwd)
        memScoped {
            execvp(commands[0], allocArrayOf(commands.map { it?.cstr?.ptr }))
        }
    } else {
        wait(null)
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,这需要处理很多c风格的代码,所以我还为此写了一个更实用的库

repositories {
    mavenCentral()
}
// add dependencies into your native target sourceSet
dependencies {
    implementation("com.kgit2:kommand:1.0.1")
}
Run Code Online (Sandbox Code Playgroud)

使用起来也非常简单

fun main(args: Array<String>) {
    val diffResult = Command("git")
    .args("diff", "HEAD^1", "$projectDir/path/to/file")
    .cwd("$projectDir")
    .spawn()
    .output()
}
Run Code Online (Sandbox Code Playgroud)