Scripting in Kotlin

Umang Chamaria
5 min readOct 16, 2023

In version 1.3.70 of Kotlin the ability to run scripts from the command line was introduced.

Hello World

Just name the file <*>.main.kts i.e. suffix the file with .main.kts and simply run the file as shown below

Note: Your machine/system should have Kotlin installed to run the script, refer to the documentation for installation steps.

kotlin my-file.main.kts 

No additional setup or installation is required to run a Kotlin Script.

Alternatively, you can make the file executable and run it as an executable file.

chmod +x my-file.main.kts
./my-file.main.kts

Why use Kotlin for Scripting?

  • Minimum, hassle-free setup.
  • Better readability when compared to Shell Scripts
  • IDE Support (IntelliJ IDEA) for features code completion, syntax highlighting/errors, etc.
  • Can easily share code between scripts.
  • Supports using any published Kotlin/Java libraries.

Let’s look at how to set Kotlin Scripting in detail.

First Kotlin Script

Create a file say hello-world.main.kts . First up, add the environment as Kotlin.

#!/usr/bin/env kotlin

Next, add the Kotlin code.

println("Hello World, from kotlin script")

The complete script would look something like below

#!/usr/bin/env kotlin # setting up the kotlin environment
// Kotlin code.
println("Hello World, from kotlin script")

Running the script gives the output as follows.

kotlin hello-world.main.kts
# Output
Hello World, from kotlin script

We can add more complex business logic to the script and get a lot of things done like maybe quickly analyzing some data, etc.

The real power of Kotlin scripting is easily sharing code across scripts, using a published third-party java/kotlin library, etc.

In the following sections, we will look at each of these features and more.

Kotlin Script with command line arguements

Like in shell script or any other script Kotlin scripts also support taking input parameters as arguments while running the script. These arguments are stored in the args variable which is of typeArray<String> .

The below script demonstrates how to access the command line arguments.

#!/usr/bin/env kotlin

/**
* This scripts demonstrates how to access command line arguments passed while running a kotlin script.
* If arguments are not passed the snippet prints no arguments found else prints out all the arguments.
*/

if (args.isEmpty()) {
println("No command line arguments passed")
} else {
println("Arguments passed: ")
for (arg in args) {
println(arg)
}
}

To pass arguments, run the script as follows

kotlin command-line-arguements.main.kts arg1 arg2 arg3

#Output
Arguments passed:
arg1
arg2
arg3

Kotlin Script with external file dependency

Kotlin scripts support importing code from external/other scripts. This enables us to reuse code/scripts and reduces the development effort. This feature can help us easily build reusable deployment pipelines, etc.

To import an external script add the @file:Import() at the top of the file and pass the filename(s)/path(s) as argument.

@file:Import("<file1>", "<file2>", "<file3>")

The script that is being imported will be a regular script, no additional setup is required.

Let’s look at an example, let’s call the main file external-script-import.main.kts

#!/usr/bin/env kotlin

/**
* Path to the file(s) to be imported.
* You can import multiple files like
* @file:Import("<file1>", "<file2>", "<file3>")
*/
@file:Import("common-utils.main.kts")

// The method is defined in the file that was imported.
executeCommandOnShell("ls")

The common-utils.main.kts would look like the following

#!/usr/bin/env kotlin

/**
* This is a common utility script that can be imported by other scripts.
*/

/**
* Executes the given command on bash shell and prints the output on the standard output i.e. terminal
* @param command command to be executed on the bash shell.
*/
fun executeCommandOnShell(command: String) {
val process = ProcessBuilder("/bin/bash", "-c", command).inheritIO().start()
process.waitFor()
}

To run the script, just run external-script-import.main.kts no need to invoke or run common-utils.main.kts

kotlin external-script-import.main.kts
# Output
command-line-arguments.main.kts common-utils.main.kts external-script-import.main.kts hello-world.main.kts

Kotlin Script with library dependency

Kotlin scripts allow importing external Kotlin/Java libraries in the script and let us use the library as in any other Kotlin/Java project. This feature helps us do more with the scripts without writing a lot of boilerplate code. For example, assume you are writing a script that GETs some data from a REST API and processes it. Instead of writing your own HTTP client using HttpUrlConnection you can import okhttp or any other networking library and use it in your script to avoid the boilerplate. Similarly, to parse the API response use moshi , gson , etc.

To import an external dependency add the repository co-ordinates using @file:Repository() i.e. link to the repository where the library is published. Add the dependency co-ordinates to the @file:DependsOn() . Both the co-ordinates should be added to the top of the file.

Note: MavenCentral repository is added by default, adding the repository for libraries published to MavenCentral is not required.

// repository link(s)
@file:Repository("<repository-link1>", "<repository-link2>", "<repository-link3>")
// library dependency
@file:DependsOn("<group-id:artifact-id:version>", "<group-id:artifact-id:version>", "<group-id:artifact-id:version>")

Let’s look at an example,

#!/usr/bin/env kotlin

/**
* This script demonstrates including library dependency.
* The script reads contents on the file using okio library and converts the JSON content to a data class using Gson.
*/

// Link(s) to the repository
@file:Repository("https://repo.maven.apache.org/maven2/")
// Library Dependencies
@file:DependsOn(
"com.squareup.okio:okio:3.6.0",
"com.google.code.gson:gson:2.10.1"
)

import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import okio.FileSystem
import okio.Path.Companion.toPath
import okio.buffer
import java.lang.StringBuilder

data class Item(val key1: String, val key2: String)
class Items(val items: Array<Items>)

/**
* Read the contents of a file and returns the contents as a string.
*/
fun readFile(fileName: String): String {
val fileContent = StringBuilder()
FileSystem.SYSTEM.source(fileName.toPath()).use { fileSource ->
fileSource.buffer().use { bufferedFileSource ->
while (true) {
val line = bufferedFileSource.readUtf8Line() ?: break
fileContent.append(line)
}
}
}
return fileContent.toString()
}

val fileData = readFile("sample.json")
val typeToken = object : TypeToken<List<Item>>() {}.type
val items = Gson().fromJson<List<Item>>(fileData, typeToken)
println(items)

Editor Support

One of the important things for any programming language is tooling support and how it makes it easy for developers to write code and build software in that language. IntelliJ IDEA has great support for Kotlin Scripting. It provides auto-complete for both Kotlin APIs and imported dependencies, supports auto-import, shows compilation errors, validates dependency paths, etc. This makes it easier to write scripts in Kotlin.

Setup IntelliJ IDEA for Scripting

To create a separate project for scripts do the following steps

  • Create an Empty Project
  • Goto File → Project Structure → Project Settings → Project
  • Set the SDK to Java Distribution on your system.

That’s it done. Your project and IDE are ready for scripting, with all the features mentioned above and more.

To add Kotlin Scripts to any existing Kotlin/Java project no additional setup is required. Just create a file with the suffix .main.kts and start scripting.

All the snippets in the article can be found in the GitHub Repository.

You can also refer to the GitHub Repository from Kaushik Gopal for more samples on Kotlin Scripting.

Happy Scripting

--

--