Execing Processes in Kotlin

Here’s the translation of the Go code to Kotlin, with explanations in Markdown format suitable for Hugo:

In the previous example, we looked at spawning external processes. We do this when we need an external process accessible to a running Kotlin process. Sometimes we just want to completely replace the current Kotlin process with another (perhaps non-Kotlin) one. To do this we’ll use Kotlin’s implementation of the classic exec function.

import java.io.File
import kotlin.system.exitProcess

fun main() {
    // For our example we'll exec `ls`. Kotlin doesn't have a direct equivalent to
    // Go's exec.LookPath, so we'll assume `ls` is in the PATH.
    val binary = "ls"

    // We'll give `ls` a few common arguments. Note that the first argument should
    // be the program name.
    val args = listOf("ls", "-a", "-l", "-h")

    // We need a set of environment variables to use. Here we just provide our current
    // environment.
    val env = System.getenv()

    // Here's the actual ProcessBuilder call. If this call is successful, the execution
    // of our process will end here and be replaced by the `ls -a -l -h` process.
    try {
        val process = ProcessBuilder(args)
            .inheritIO()
            .directory(File("."))
            .apply { environment().putAll(env) }
            .start()
        
        val exitCode = process.waitFor()
        exitProcess(exitCode)
    } catch (e: Exception) {
        println("Error executing process: ${e.message}")
        exitProcess(1)
    }
}

When we run our program it is replaced by ls.

$ kotlinc exec_processes.kt -include-runtime -d exec_processes.jar
$ java -jar exec_processes.jar
total 16K
drwxr-xr-x  4 user user 4.0K May 15 10:30 .
drwxr-xr-x 91 user user 4.0K May 15 10:25 ..
-rw-r--r--  1 user user  985 May 15 10:30 exec_processes.kt
-rw-r--r--  1 user user 3.5K May 15 10:30 exec_processes.jar

Note that Kotlin doesn’t offer a classic Unix fork function. Usually this isn’t an issue though, since starting coroutines, spawning processes, and exec’ing processes covers most use cases for fork.

In Kotlin, we use ProcessBuilder to create and start new processes. The inheritIO() method is used to redirect the subprocess’s standard I/O to the current process. We use waitFor() to wait for the process to complete and get its exit code.

Remember that this approach doesn’t exactly replicate the behavior of syscall.Exec in Go, which replaces the current process. In Kotlin, we’re creating a new process and waiting for it to finish.