Skip to Content
Course content

3: Running Kotlin Scripts vs Compiled Programs

Click on the "Edit" button in the top corner of the screen to edit your slide content.

In the previous lessons, we looked at the syntax, but now we need to talk about how we actually run the code. In Kotlin, you have two primary paths: writing a script (.kts) or building a compiled program (.kt). I see a lot of developers get these confused, or worse, use scripts for everything because they're "faster" to start, only to hit a wall when they need to distribute their tool to a teammate.

To show you the difference, let's build a simple utility that scans a directory and warns us if any file is larger than 10MB. It's a classic "janitor" task.

Drafting a quick utility with .kts

When I just need to get something done on my own machine, I don't want to deal with project structures or build files. I use a Kotlin Script (.kts). The magic here is that scripts don't require a main function wrapper; the code is just executed from top to bottom.

// file_check.kts
import java.io.File

val threshold = 10 * 1024 * 1024 // 10MB
val dir = File("./logs")

if (!dir.exists()) {
    println("Directory not found!")
} else {
    dir.listFiles()?.forEach { file ->
        if (file.length() > threshold) {
            println("Warning: ${file.name} is too large: ${file.length()} bytes")
        }
    }
}

I can run this immediately using the command line: kotlinc -script file_check.kts. It's great for prototypes. I'm not worrying about classes or packages; I'm just telling the computer to do a task.

The "muscle memory" mistake

Here is where I usually trip up when I'm switching between languages. Out of habit, I started adding a main function to this script because that's what I do in every other Kotlin file. I wrote this:

// Oops, I did this in the .kts file
fun main() {
    // ... my logic ...
}
main() 

Now, this works, but it's redundant. In a .kts file, the script is already essentially inside a main block. By defining a main function and then calling it manually, I've just added unnecessary boilerplate. I spent five minutes wondering why my script felt clunky before I realized I was treating a script like a compiled program. I stripped the function call out and went back to the top-level code.

Hardening the tool for production

Now, imagine my teammate wants this tool. I can't expect them to have the Kotlin compiler installed just to run a script. I need to turn this into a compiled program. This means renaming the file to FileCheck.kt and wrapping the logic in a proper entry point.

// FileCheck.kt
import java.io.File

fun main(args: Array<String>) {
    val path = args.getOrNull(0) ?: "./logs"
    val threshold = 10 * 1024 * 1024 
    val dir = File(path)

    if (!dir.exists()) {
        println("Directory not found: $path")
        return
    }

    dir.listFiles()?.filter { it.length() > threshold }?.forEach { 
        println("Warning: ${it.name} is too large") 
    }
}

Notice I added args here. Scripts are fine for hardcoded paths, but a real program should be flexible. To run this, I compile it to a JVM class file (or a JAR): kotlinc FileCheck.kt -include-runtime -d FileCheck.jar. Now, I can send that .jar file to anyone with a Java Runtime Environment (JRE), and they can run it with java -jar FileCheck.jar /var/log without ever seeing my source code.

Comparing the execution flow

So, when do you choose which? I follow a simple rule of thumb:

  • Use .kts (Scripts) for one-off migrations, build scripts, or local automation. If the code is "disposable" or only for you, don't waste time compiling.
  • Use .kt (Compiled) for anything that will be used by others, anything that needs to be fast (since it's pre-compiled to bytecode), or anything that grows beyond a single file.



📋 Practical Task

Build a Log-Line Counter Utility

Your task is to create a utility that counts how many lines exist in a specific text file. You will implement this twice to practice the transition from script to program.

  1. The Script Version: Create a file named line_count.kts. Write code that reads a file named app.log (you can create a dummy text file for this) and prints the total line count. Run it using the -script flag.
  2. The Compiled Version: Convert that script into a compiled program named LineCounter.kt.
    • Wrap the logic in a fun main(args: Array<String>).
    • Modify the program so it takes the filename as a command-line argument (using args[0]) rather than hardcoding "app.log".
    • Compile it into a JAR file and run it using the java -jar command.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.