When working with Kotlin, there may be situations where you need to execute system commands and capture their output. This can be useful for interacting with the operating system, automating system tasks, and retrieving data from command-line applications. In this article, we'll explore how to execute system commands in Kotlin and capture their output effectively.
Using the ProcessBuilder Class
The ProcessBuilder class is a flexible way to start and manage system processes in Java, and it can be used seamlessly in Kotlin. Let's see a basic example of how to run a system command and capture its output:
fun executeCommand(command: String): String {
val process = ProcessBuilder(command.split(" "))
.redirectErrorStream(true)
.start()
return process.inputStream.bufferedReader().readText()
}
fun main() {
val commandOutput = executeCommand("ls -l")
println("Command Output:\n$commandOutput")
}
In the code above, we define a function executeCommand that takes a command string, splits it into a list, and then uses ProcessBuilder to execute it.
- Split Command: We split the command string to pass each argument separately to
ProcessBuilder. - Redirect Error Stream:
redirectErrorStream(true)merges standard error with standard output, allowing us to capture all output in one stream. - Read Text: We capture the output by reading from the
inputStreamof the process.
Handling Process Output and Errors Separately
Sometimes you might want to handle standard output and standard error separately. Here’s how you can achieve that:
fun executeCommandWithSeparateStreams(command: String): Pair {
val process = ProcessBuilder(command.split(" ")).start()
val output = process.inputStream.bufferedReader().readText()
val error = process.errorStream.bufferedReader().readText()
return output to error
}
fun main() {
val (output, error) = executeCommandWithSeparateStreams("ls -l")
println("Standard Output:\n$output")
println("Standard Error:\n$error")
}
This code separately captures standard output and error by reading text from both inputStream and errorStream.
Synchronous Execution and Exit Code
In some cases, you may need not only to capture the output but also to wait for the completion and get the exit code of the command:
fun executeCommandAndWait(command: String): Int {
val process = ProcessBuilder(command.split(" ")).start()
val exitCode = process.waitFor()
val output = process.inputStream.bufferedReader().readText()
println("Output:\n$output")
return exitCode
}
fun main() {
val exitCode = executeCommandAndWait("ls -l")
println("Exit Code: $exitCode")
}
Here, waitFor() is used to block the current thread until the process terminates. This is useful when you need to ensure the completion of the command execution before proceeding. With these examples, you've learned how to capture and manage the output from system commands using Kotlin. Whether you want a quick output capture or detailed error and process handling, Kotlin’s interoperability with Java’s ProcessBuilder class offers powerful tools for system interaction.