Scripting in Kotlin
In version 1.3.70
of Kotlin the ability to run scripts from the command line was introduced.
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