Chapters

Hide chapters

Kotlin Apprentice

Second Edition · Android 10 · Kotlin 1.3 · IDEA

Before You Begin

Section 0: 3 chapters
Show chapters Hide chapters

Section III: Building Your Own Types

Section 3: 8 chapters
Show chapters Hide chapters

Section IV: Intermediate Topics

Section 4: 9 chapters
Show chapters Hide chapters

24. Scripting with Kotlin
Written by Ellen Shapiro

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

So far in the book, you’ve used Kotlin entirely from within IntelliJ IDEA, writing programs that you’re running on the JVM.

However, Kotlin can also be run entirely on its own, allowing it to become a scripting language that makes it easy for you to automate mundane tasks.

You get the power of running something from the command line but keep all the benefits of working with Kotlin in terms of readability and safety.

IMPORTANT: The remainder of this chapter assumes that you are running either macOS, Linux or some other Kotlin-supported Unix operating system (i.e., FreeBSD or Solaris). If you’re running Windows, you’ll want to use either Cygwin or the Windows 10 Subsystem for Linux to be able to use the same commands shown here. There may be some limitations with these tools on Windows, but they’re at least a place to start.

What is scripting?

Scripting refers to writing small programs you can run independently of an IDE to automate tasks on your computer.

A script is the small program that you write and run. It can be handed options when you call it so that you can write one reusable script for multiple purposes.

You’ll often hear people talk about shell scripting, which is using .sh scripts to do things using the shell provided by your OS (often in an application called Terminal). Common shells are bash, zsh, and fish.

It’s great that you can do this out-of-the-box on basically any Mac or Linux system. However, there are a number of issues with shell scripting that have led developers to pursue alternatives:

  • Shell scripting is not type-safe. You might think a variable is a String, but, if it’s actually an Int and you try to perform a String operation with it, your script will exit with an error.
  • Shell scripting is not compiled. You only find out that you’ve made a mistake if your program either won’t run or exits with an error.
  • Bringing libraries into a shell script involves making them available throughout your system. This may not be behavior you want for many reasons, including security.
  • Shell scripts can be very difficult to read. Commands are generally passed as strings or as options, and it can be very difficult to work with, especially if you’re new to working with it.

Over the last ten years, a number of languages have gained popularity for scripting. Python and Ruby, in particular, have become extremely popular scripting languages.

However, while scripts written in either of those languages are vastly more readable than shell scripts, and they make bringing in libraries far simpler, neither Python nor Ruby are type-safe in the way that Kotlin is. Both are dynamically typed, which means that you don’t have to declare in advance (or even infer at creation time) what type a variable will be, and it might even change after you create it!

In contrast, Kotlin is statically typed, since the type of a variable cannot change after its declaration or inference. For example, when you write the following:

val three = 3

The variable three is inferred to have a type of Int. If you were to write:

val three: String = 3

You’d get an error, since you’ve explicitly declared the type of three to be a String, and the value 3 is not a String, it’s an Int. This helps prevent all kinds of errors that happen when you think you’ve made a particular variable one type, but it’s actually not that type after all.

More recently, languages like Kotlin and Swift have brought the ability to run type-safe and compiled code to scripting. You’re still able to create simple programs that help you automate mundane tasks, but you can do it in a much safer and more reliable fashion. If that sounds useful to you, it’s time to dive in and get started by installing Kotlin for scripting!

Installing Kotlin for scripting

Up to this point, your computer has been accessing Kotlin through your IDE, IntelliJ IDEA. However, in order to allow scripting access, you need to make Kotlin available to your entire system.

Installing SDKMan!

Note: If you’ve already got SDKMan! installed, skip to the “Installing Kotlin” section below.

curl -s https://get.sdkman.io | bash

Installing Kotlin

Open a new Terminal — either a new window or a new tab if your shell program allows it — and install Kotlin using SDKMan! by typing the following command and pressing Enter:

sdk install kotlin
which kotlin
/Users/[username]/.sdkman/candidates/kotlin/current/bin/kotlin
kotlinc-jvm

Using the REPL

A REPL is essentially a tiny Kotlin program in which you can type things and have them execute immediately. When it launches, you’ll see something like this:

println("Hello, world!")

val groceries = listOf("apples", "ground beef", "toilet paper")
groceries.joinToString("\n")
apples
ground beef
toilet paper
groceries.map { it.count() }
[6, 11, 12]
data class Grocery(val name: String, val cost: Float)
val moreGroceries = listOf(Grocery("apples", 0.50f), Grocery("ground beef", 5.25f), Grocery("toilet paper", 2.23f))
val cost = moreGroceries.fold(0.0f) { running, next -> running + next.cost }
println("Your groceries cost $cost")
Your groceries cost 7.98
:quit
kotlinc-jvm
println("$groceries")

Creating script files

Kotlin script files are a unique type of Kotlin file. They compile as if the entire file is the main() function for a Kotlin program.

Running a script from the command line

First, go in your computer’s file browser to the starter directory for this chapter. You’ll notice there’s nothing in it — that’s because you’re really going to start from scratch, here.

touch script.kts
println("Hello, scripting!")
kotlinc -script script.kts
Hello, scripting!

Running a script with IntelliJ IDEA

Quit your generic text editor and open up IntelliJ IDEA. If you close your other projects, you should land on this screen:

Hello, scripting!

Handling arguments

An argument, when it comes to running a Kotlin script, is a string that you enter into the command line. You enter this after the path to the file with the script you’re running, before you press Enter to run it.

if (args.isEmpty()) {
  println("[no args]")
} else {
  println("Args:\n ${args.joinToString("\n ")}")
}
[no args] 

hello
Args:
 hello

Args:
 hello
 Ellen Shapiro
kotlinc -script script.kts
[no args] 
kotlinc -script script.kts Kotlin scripting is awesome
Args:
 Kotlin
 scripting
 is
 awesome
kotlinc -script script.kts "Kotlin scripting is awesome"
Args:
 Kotlin scripting is awesome

Getting information from the system

Getting information about the filesystem is really helpful because you can use it in many different ways: moving files around, copying files, and figuring out how large files are or where they’re located.

fun currentFolder(): File {
  return File("").absoluteFile
}
import java.io.File
val current = currentFolder()
println("Current folder: $current")
Current folder: [fullpath]/KotlinApprentice/scripting-with-kotlin/projects/starter
fun File.contents(): List<File> {
  return this.listFiles().toList()
}
val current = currentFolder()
println("Current folder contents:\n ${current.contents().joinToString("\n ")}")
Current folder contents:
 [fullpath]/KotlinApprentice/scripting-with-kotlin/projects/starter/.DS_Store
 [fullpath]/KotlinApprentice/scripting-with-kotlin/projects/starter/script.kts
 [fullpath]/KotlinApprentice/scripting-with-kotlin/projects/starter/.idea
fun File.fileNames(): List<String> {
  return this.contents().map { it.name }
}
println("Current folder contents:\n ${current.fileNames().joinToString("\n ")}")
Current folder contents:
 .DS_Store
 script.kts
 .idea
fun File.folders(): List<File> {
  return this.contents().filter { it.isDirectory }
}

fun File.files(): List<File> {
  return this.contents().filter { it.isFile }
}
fun File.fileNames(): List<String> {
  return this.files().map { it.name }
}
fun File.folderNames(): List<String> {
  return this.folders().map { it.name }
}
fun File.printFolderInfo() {
  // 1
  println("Contents of `${this.name}`:")
  
  // 2
  if (this.folders().isNotEmpty()) {
    println("- Folders:\n   ${this.folderNames().joinToString("\n   ")}")
  }

  // 3
  if (this.files().isNotEmpty()) {
    println("- Files:\n   ${this.fileNames().joinToString("\n   ")}")
  }

  // 4
  println("Parent: ${this.parentFile.name}")
}
current.printFolderInfo()
Contents of `starter`:
- Folders:
   .idea
- Files:
   .DS_Store
   script.kts
Parent: projects
fun valueFromArgsForPrefix(prefix: String): String? {
  val arg = args.firstOrNull { it.startsWith(prefix) }

  if (arg == null) return null

  val pieces = arg.split("=")
  return if (pieces.size == 2) {
    pieces[1]
  } else {
    null
  }
}
val folderPrefix = "folder="
val folderValue = valueFromArgsForPrefix(folderPrefix)
if (folderValue != null) {
  val folder = File(folderValue).absoluteFile
  folder.printFolderInfo()
} else {
  println("No path provided, printing working directory info")
  currentFolder().printFolderInfo()
}
pwd
/Users/ellen/Desktop/Wenderlich/KotlinApprentice/scripting-with-kotlin/projects/starter
kotlinc -script script.kts "Kotlin scripting is awesome"
No path provided, printing working directory info
Contents of `starter`:
- Folders:
   .idea
- Files:
   .DS_Store
   script.kts
Parent: projects
kotlinc -script script.kts "Kotlin scripting is awesome" folder=/Users/ellen/Desktop/Wenderlich/KotlinApprentice/scripting-with-kotlin/projects/starter
Contents of `projects`:
- Folders:
   starter
   final
   challenge
- Files:
   .DS_Store

Challenges

Key points

  • Scripting is writing small programs in a text editor that can be run from the command line and be used to do various types of processing on your computer.
  • As a scripting language, Kotlin gives you the static-typing lacking in other scripting languages like Python and Ruby.
  • Kotlin comes with a Read-Evaluate-Print Loop or REPL that can be used to investigate Kotlin code in an interactive manner.
  • Kotlin scripts end with the extension .kts, as opposed to normal Kotlin code that ends with .kt.
  • You can use IntelliJ IDEA as a script editor, and then either run your scripts within the IDE or from a command line shell on your OS.
  • Kotlin scripts run inside a hidden main() function and can access args passed into the script at the command line. Scripts can also import Kotlin and Java libraries to access their features.
  • You can use Kotlin scripts to read and write to the files and folders on your filesystem, and much more!

Where to go from here?

The tool kscript provides a convenient wrapper around Kotlin scripting. It allows pre-compilation of scripts, which results in much faster iteration, and it also allows you to use a simpler syntax for accessing Kotlin at runtime. The creator gave a talk at KotlinConf 2017 which is worth watching for a great outline of some of the problems he was trying to solve.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now