Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin-native execute command and get the output

I'd like to know if there's a way in kotlin native to call a command via posix and receive it's terminal output. For example, I'd like to get the "git diff" command working without having to create a temporary file, write output to it and then read from that file.

On SO I've only found solutions requiring ProcessBuilder, which isn't available on kotlin-native, as it's a Java library.

like image 583
MG lolenstine Avatar asked Mar 23 '26 18:03

MG lolenstine


2 Answers

I found a working piece of code I wanted to use, so I'm posting it here for future viewers!

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()
}
like image 152
MG lolenstine Avatar answered Mar 25 '26 16:03

MG lolenstine


Usually you can use the POSIX api and use fork and wait and some I/O related functions for your purpose

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)
    }
}

Of course, this needs to deal with a lot of c-style code, so I also wrote a more practical library for this

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

It is also very simple to use

fun main(args: Array<String>) {
    val diffResult = Command("git")
    .args("diff", "HEAD^1", "$projectDir/path/to/file")
    .cwd("$projectDir")
    .spawn()
    .output()
}
like image 33
bppleman Avatar answered Mar 25 '26 16:03

bppleman