Kotlin Scripting

Kotlin scripting allows developers to write and execute scripts to automate tasks, perform quick computations, and more. This article will explore the essentials of Kotlin scripting, including writing and executing scripts, using them for automation, and best practices for scripting.

1. Introduction to Kotlin Scripting

Kotlin scripting offers a way to write short and concise scripts without the need for a full application structure. It’s particularly useful for tasks such as automation, quick data processing, and prototyping. Kotlin scripts are typically written with the .kts extension.

1.1 Benefits of Kotlin Scripting

  • Conciseness: Kotlin’s concise syntax makes scripts easier to write and read.
  • Interoperability: Kotlin can use existing Java libraries, making it powerful for various tasks.
  • Tooling Support: Kotlin scripts can be edited and executed in environments like IntelliJ IDEA, which provides excellent tooling support.

1.2 Getting Started with Kotlin Scripting

To get started with Kotlin scripting, you need to have Kotlin installed on your machine. You can use the Kotlin command-line tools or an Integrated Development Environment (IDE) like IntelliJ IDEA.

Example: Hello World Script

Kotlin
println("Hello, World!")

Executing the Script

Save the above code in a file named hello.kts and run it using the Kotlin command-line tool:

Kotlin
kotlinc -script hello.kts

Output

Kotlin
Hello, World!

2. Writing and Executing Kotlin Scripts

2.1 Basic Syntax and Structure

Kotlin scripts are written in a similar manner to Kotlin code files but do not require the enclosing fun main() function or class definitions. This allows for more direct and imperative code execution.

Example: Variable Declaration and Function

Kotlin
val greeting = "Hello"
fun greet(name: String) = "$greeting, $name!"

println(greet("Kotlin"))

Executing the Script

Save the above code in a file named greet.kts and run it using the Kotlin command-line tool:

Kotlin
kotlinc -script greet.kts

Output

Kotlin
Hello, Kotlin!

2.2 Using Libraries in Scripts

Kotlin scripts can utilize any Java or Kotlin library by specifying the dependencies. This can be done using the @file:DependsOn annotation.

Example: Using an External Library

  1. Add Dependency

First, add the dependency to your script:

Kotlin
@file:DependsOn("com.github.kittinunf.fuel:fuel:2.3.1")
import com.github.kittinunf.fuel.httpGet
import com.github.kittinunf.result.Result

val url = "https://jsonplaceholder.typicode.com/posts/1"
val (_, _, result) = url.httpGet().responseString()

when (result) {
    is Result.Failure -> {
        val ex = result.getException()
        println(ex)
    }
    is Result.Success -> {
        val data = result.get()
        println(data)
    }
}
  1. Executing the Script

Save the above code in a file named fetch.kts and run it using the Kotlin command-line tool:

Kotlin
kotlinc -script fetch.kts

Output

Kotlin
{
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit..."
}

3. Scripting for Automation Tasks

3.1 Automating File Operations

Kotlin scripts can automate file operations such as reading, writing, and processing files.

Example: File Processing Script

Kotlin
import java.io.File

val inputFile = File("input.txt")
val outputFile = File("output.txt")

val content = inputFile.readText()
val processedContent = content.toUpperCase()

outputFile.writeText(processedContent)
println("File processing complete. Check 'output.txt'.")

Executing the Script

Save the above code in a file named fileProcessing.kts and run it using the Kotlin command-line tool:

Kotlin
kotlinc -script fileProcessing.kts

Output

Kotlin
File processing complete. Check 'output.txt'.

3.2 Scheduling Tasks

Kotlin scripts can be used with task schedulers (like cron jobs on Unix systems) to perform regular tasks.

Example: Backup Script

Kotlin
import java.nio.file.*
import java.time.LocalDateTime

val sourceDir = Paths.get("sourceDir")
val backupDir = Paths.get("backupDir", LocalDateTime.now().toString())

Files.createDirectories(backupDir)
Files.walk(sourceDir).forEach { path ->
    val targetPath = backupDir.resolve(sourceDir.relativize(path))
    if (Files.isDirectory(path)) {
        Files.createDirectories(targetPath)
    } else {
        Files.copy(path, targetPath, StandardCopyOption.REPLACE_EXISTING)
    }
}
println("Backup completed to $backupDir")

Executing the Script

Save the above code in a file named backup.kts and run it using the Kotlin command-line tool:

Kotlin
kotlinc -script backup.kts

Output

Kotlin
Backup completed to backupDir/2023-05-18T20:35:48.076

4. Scripting Best Practices

4.1 Keep Scripts Simple and Focused

  • Single Responsibility: Each script should perform a single task or a related group of tasks.
  • Readability: Write clean and readable code. Use comments and meaningful variable names.

4.2 Error Handling and Logging

  • Handle Exceptions: Ensure that scripts handle potential exceptions gracefully.
  • Logging: Use logging to keep track of script execution and errors.

Example: Error Handling and Logging

Kotlin
import java.io.File
import java.util.logging.Logger

val logger = Logger.getLogger("ScriptLogger")

try {
    val file = File("nonexistent.txt")
    val content = file.readText()
    println(content)
} catch (e: Exception) {
    logger.severe("Error reading file: ${e.message}")
}

Executing the Script

Save the above code in a file named errorHandling.kts and run it using the Kotlin command-line tool:

Kotlin
kotlinc -script errorHandling.kts

Output

Kotlin
SEVERE: Error reading file: nonexistent.txt (No such file or directory)

4.3 Modularize Scripts

  • Reusable Functions: Break down scripts into reusable functions.
  • Script Libraries: Create libraries of common functions and utilities.

Example: Modular Script

  1. Utility Script (utils.kts)
Kotlin
fun greet(name: String) = "Hello, $name!"
  1. Main Script
Kotlin
@file:Import("utils.kts")
println(greet("Kotlin"))

Executing the Script

Save the utility script in a file named utils.kts and the main script in a file named main.kts. Then run the main script using the Kotlin command-line tool:

Kotlin
kotlinc -script main.kts

Output

Kotlin
Hello, Kotlin!

1. Monitoring Disk Usage

This script checks the disk usage of a specified directory and prints a warning if it exceeds a certain threshold.

Script: DiskUsageMonitor.kts

Kotlin
import java.io.File

val directory = File("/home/avishik/tutcoach.com/monitor")
val threshold = 80 // percentage

fun getDiskUsagePercentage(directory: File): Int {
    val totalSpace = directory.totalSpace
    val freeSpace = directory.freeSpace
    val usedSpace = totalSpace - freeSpace
    return (usedSpace.toDouble() / totalSpace * 100).toInt()
}

val usage = getDiskUsagePercentage(directory)
println("Disk usage for ${directory.path}: $usage%")

if (usage > threshold) {
    println("Warning: Disk usage exceeds $threshold%")
}

Expected Output

Assuming the disk usage is 85%:

Kotlin
Disk usage for /home/avishik/tutcoach.com/monitor: 85%
Warning: Disk usage exceeds 80%

2. System Uptime Check

This script checks the system uptime and prints it.

Script: SystemUptime.kts

Kotlin
import java.io.File

fun getSystemUptime(): Long {
    val uptimeFile = File("/proc/uptime")
    val uptimeSeconds = uptimeFile.readText().split(" ")[0].toDouble()
    return uptimeSeconds.toLong()
}

val uptime = getSystemUptime()
println("System uptime: $uptime seconds")

Expected Output

Assuming the system has been up for 123456 seconds:

Kotlin
System uptime: 123456 seconds

3. File Cleanup Script

This script deletes files older than a certain number of days in a specified directory.

Script: FileCleanup.kts

Kotlin
import java.io.File
import java.time.Instant
import java.time.temporal.ChronoUnit

val directory = File("/path/to/cleanup")
val daysOld = 30L

fun deleteOldFiles(directory: File, daysOld: Long) {
    val threshold = Instant.now().minus(daysOld, ChronoUnit.DAYS).toEpochMilli()
    directory.listFiles()?.forEach { file ->
        if (file.isFile && file.lastModified() < threshold) {
            if (file.delete()) {
                println("Deleted: ${file.path}")
            } else {
                println("Failed to delete: ${file.path}")
            }
        }
    }
}

deleteOldFiles(directory, daysOld)

Expected Output

Assuming there are two old files, oldfile1.txt and oldfile2.txt, and they are successfully deleted:

Kotlin
Deleted: /path/to/cleanup/oldfile1.txt
Deleted: /path/to/cleanup/oldfile2.txt

4. Automated Backup Script

This script backs up a specified directory to a backup location.

Script: BackupScript.kts

Kotlin
import java.nio.file.*
import java.time.LocalDateTime

val sourceDir = Paths.get("/home/avishik/tutcoach.com/demo")
val backupDir = Paths.get("/home/avishik/tutcoach.com/dest", LocalDateTime.now().toString())

fun backupDirectory(source: Path, destination: Path) {
    Files.createDirectories(destination)
    Files.walk(source).forEach { path ->
        val targetPath = destination.resolve(source.relativize(path))
        if (Files.isDirectory(path)) {
            Files.createDirectories(targetPath)
        } else {
            Files.copy(path, targetPath, StandardCopyOption.REPLACE_EXISTING)
        }
    }
    println("Backup completed to $destination")
}

backupDirectory(sourceDir, backupDir)

Expected Output

Assuming the backup is completed successfully to a directory named after the current date and time:

Kotlin
Backup completed to /home/avishik/tutcoach.com/dest/2024-05-21T15:30:45.678

5. User Management Script

This script lists all users on the system and checks if a specified user exists.

Script: UserManagement.kts

Kotlin
import java.io.File

val passwdFile = File("/etc/passwd")
val targetUser = "username"

fun listUsers(passwdFile: File): List<String> {
    return passwdFile.readLines().map { it.split(":")[0] }
}

fun checkUserExists(users: List<String>, targetUser: String): Boolean {
    return users.contains(targetUser)
}

val users = listUsers(passwdFile)
println("All users: $users")

if (checkUserExists(users, targetUser)) {
    println("User '$targetUser' exists.")
} else {
    println("User '$targetUser' does not exist.")
}

Expected Output

Assuming the target user username exists and the system has the following users: root, admin, username:

Kotlin
All users: [root, admin, username]
User 'username' exists.

These scripts and their outputs demonstrate how Kotlin can be effectively used for system administration tasks. Each script performs a specific function, providing useful outputs that help in

Conclusion

Kotlin scripting is a powerful tool for automating tasks, performing quick computations, and prototyping. With its concise syntax and interoperability with Java libraries, Kotlin provides an efficient way to write and execute scripts. By following best practices such as keeping scripts simple, handling errors, and modularizing code, developers can create robust and maintainable scripts for various automation tasks.