Quando alguém fala em Kotlin, a primeira associação geralmente é Android ou backend com Spring Boot. Mas existe um lado menos explorado da linguagem que pode mudar sua produtividade no dia a dia: Kotlin Script. Com arquivos .main.kts, você escreve e executa scripts diretamente — sem compilar, sem criar projeto, sem main(). Basta um arquivo e o comando kotlin no terminal.

Neste artigo, vamos explorar como usar Kotlin como linguagem de scripting para automação, tarefas DevOps, processamento de dados e tudo mais que você hoje faz com Bash ou Python. Se você já programa em Kotlin, vai aproveitar todo o type safety, null safety e a biblioteca padrão que já conhece — agora em scripts rápidos e descartáveis.

O que é Kotlin Script?

Kotlin Script é a capacidade de executar código Kotlin diretamente, sem precisar de um projeto estruturado com build.gradle.kts, sem declarar uma função main() e sem etapa explícita de compilação. Você cria um arquivo com extensão .main.kts, escreve código top-level e executa com kotlin script.main.kts.

O formato .main.kts é o padrão oficial mantido pela JetBrains. Ele suporta:

  • Código top-level (sem precisar de classes ou funções main)
  • Importação de dependências Maven via anotações @file:DependsOn
  • Resolução automática de repositórios com @file:Repository
  • Imports implícitos da biblioteca padrão do Kotlin
  • Suporte nativo no IntelliJ IDEA com autocomplete e highlighting

Se você já trabalha com Gradle Kotlin DSL, já usa Kotlin Script sem perceber — os arquivos build.gradle.kts e settings.gradle.kts são scripts Kotlin.

Primeiros passos

Pré-requisitos

Você precisa do Kotlin instalado na máquina. Se já tem o IntelliJ IDEA, o compilador vem junto. Caso contrário, instale via SDKMAN:

// Terminal
// sdk install kotlin

Ou via Homebrew no macOS:

// Terminal
// brew install kotlin

Verifique a instalação:

// Terminal
// kotlin -version
// Kotlin version 2.3.20-release (JRE 21.0.3+9)

Se você quer um guia completo de setup, confira como instalar Kotlin.

Seu primeiro script

Crie um arquivo chamado ola.main.kts:

// ola.main.kts
val nome = "Kotlin Brasil"
val versao = KotlinVersion.CURRENT

println("Olá do $nome!")
println("Rodando Kotlin $versao")
println("Data: ${java.time.LocalDate.now()}")

Execute no terminal:

// Terminal
// kotlin ola.main.kts
// Olá do Kotlin Brasil!
// Rodando Kotlin 2.3.20
// Data: 2026-04-24

Nenhum fun main(), nenhum class, nenhum build. O código roda direto. A primeira execução pode demorar alguns segundos para compilação em cache, mas execuções subsequentes são muito mais rápidas.

Adicionando dependências externas

A feature mais poderosa do .main.kts é a capacidade de importar bibliotecas Maven diretamente no script com anotações:

// relatorio.main.kts
@file:DependsOn("com.squareup.okhttp3:okhttp:4.12.0")
@file:DependsOn("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1")

import okhttp3.OkHttpClient
import okhttp3.Request
import kotlinx.serialization.json.*

val client = OkHttpClient()
val request = Request.Builder()
    .url("https://api.github.com/repos/JetBrains/kotlin")
    .build()

val response = client.newCall(request).execute()
val json = Json.parseToJsonElement(response.body!!.string())

val stars = json.jsonObject["stargazers_count"]
val forks = json.jsonObject["forks_count"]
val language = json.jsonObject["language"]

println("Repositório Kotlin no GitHub:")
println("  Estrelas: $stars")
println("  Forks: $forks")
println("  Linguagem: $language")

As dependências são baixadas automaticamente do Maven Central na primeira execução e cacheadas para as próximas. Se precisar de um repositório diferente, use @file:Repository:

@file:Repository("https://jitpack.io")
@file:DependsOn("com.github.usuario:biblioteca:1.0.0")

Isso é semelhante ao que fazemos no Gradle Version Catalog, mas para scripts avulsos.

Casos de uso práticos

Automação de deploy

Um script para verificar o status de containers Docker e notificar via webhook:

// check-deploy.main.kts
@file:DependsOn("com.squareup.okhttp3:okhttp:4.12.0")

import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.MediaType.Companion.toMediaType

data class Container(val nome: String, val status: String, val uptime: String)

fun verificarContainers(): List<Container> {
    val process = ProcessBuilder("docker", "ps", "--format", "{{.Names}}|{{.Status}}")
        .redirectErrorStream(true)
        .start()

    return process.inputStream.bufferedReader().readLines()
        .filter { it.isNotBlank() }
        .map { linha ->
            val partes = linha.split("|")
            Container(
                nome = partes[0],
                status = if (partes[1].startsWith("Up")) "running" else "stopped",
                uptime = partes[1]
            )
        }
}

val containers = verificarContainers()
val parados = containers.filter { it.status == "stopped" }

if (parados.isNotEmpty()) {
    println("ALERTA: ${parados.size} container(s) parado(s):")
    parados.forEach { println("  - ${it.nome}: ${it.uptime}") }
} else {
    println("Todos os ${containers.size} containers estão rodando.")
}

Se você trabalha com Kubernetes ou Docker, scripts como esse substituem Bash com a vantagem de type safety e tratamento de erros robusto.

Processamento de CSV

Ler um CSV de funcionários e gerar estatísticas — algo que muitos fazem em Python:

// analise-salarios.main.kts
import java.io.File

data class Funcionario(
    val nome: String,
    val departamento: String,
    val salario: Double,
    val experiencia: Int
)

val funcionarios = File("funcionarios.csv")
    .readLines()
    .drop(1) // pular header
    .map { linha ->
        val campos = linha.split(",")
        Funcionario(
            nome = campos[0].trim(),
            departamento = campos[1].trim(),
            salario = campos[2].trim().toDouble(),
            experiencia = campos[3].trim().toInt()
        )
    }

println("Total de funcionários: ${funcionarios.size}")
println("Salário médio: R$ ${"%.2f".format(funcionarios.map { it.salario }.average())}")
println()

// Agrupamento por departamento
funcionarios.groupBy { it.departamento }
    .toSortedMap()
    .forEach { (depto, funcs) ->
        val media = funcs.map { it.salario }.average()
        println("$depto: ${funcs.size} pessoas, média R$ ${"%.2f".format(media)}")
    }

println()

// Top 5 salários
println("Top 5 salários:")
funcionarios.sortedByDescending { it.salario }
    .take(5)
    .forEachIndexed { i, f ->
        println("  ${i + 1}. ${f.nome} (${f.departamento}): R$ ${"%.2f".format(f.salario)}")
    }

Perceba como as collections do KotlingroupBy, sortedByDescending, map, average — tornam o processamento de dados expressivo e conciso, comparável ao Python.

Geração de código e scaffolding

Criar arquivos de projeto automaticamente:

// scaffold.main.kts
import java.io.File

val pacote = args.getOrElse(0) { "com.exemplo" }
val nome = args.getOrElse(1) { "MeuProjeto" }

val diretorios = listOf(
    "src/main/kotlin/${pacote.replace('.', '/')}",
    "src/main/resources",
    "src/test/kotlin/${pacote.replace('.', '/')}",
    "src/test/resources"
)

diretorios.forEach { File(it).mkdirs() }

File("src/main/kotlin/${pacote.replace('.', '/')}/Application.kt").writeText("""
package $pacote

fun main() {
    println("$nome iniciado com sucesso!")
}
""".trimIndent())

File("build.gradle.kts").writeText("""
plugins {
    kotlin("jvm") version "2.3.20"
    application
}

group = "$pacote"
version = "1.0.0"

application {
    mainClass.set("$pacote.ApplicationKt")
}

repositories {
    mavenCentral()
}

dependencies {
    testImplementation(kotlin("test"))
}
""".trimIndent())

println("Projeto '$nome' criado com sucesso!")
println("Pacote: $pacote")
println("Diretórios criados: ${diretorios.size}")

Execute com: kotlin scaffold.main.kts com.kotlinbrasil MeuApp

Kotlin Script vs Bash vs Python

Uma comparação honesta para decidir quando usar cada um:

Kotlin Script vence quando

  • Você já é dev Kotlin e quer aproveitar o conhecimento existente
  • O script precisa de type safety e tratamento de null safety robusto
  • Você quer usar bibliotecas Java/Kotlin existentes (OkHttp, kotlinx-serialization, etc.)
  • O script pode crescer e eventualmente virar um projeto — a migração é trivial
  • Integração com CI/CD onde o ambiente já tem Kotlin

Bash vence quando

  • São operações simples de sistema de arquivos (mover, copiar, renomear)
  • Pipes e redirecionamento são a ferramenta certa
  • O ambiente pode não ter JVM instalada
  • O script tem menos de 20 linhas

Python vence quando

  • Data science pesado com NumPy, Pandas, matplotlib
  • O time não conhece Kotlin
  • Prototipagem rápida de ML (embora o Kotlin Notebook esteja fechando essa lacuna)

Se você quer uma comparação mais profunda entre Kotlin e Python como linguagens, confira nosso artigo Kotlin vs Python. Para automação e scripting, Python continua sendo referência, mas o Kotlin Script brilha quando você quer type safety e acesso ao ecossistema JVM. Já para automação de infra e CLIs performáticas, Go é uma alternativa forte com binários estáticos e compilação rápida.

Dicas avançadas

Shebang para execução direta

No Linux e macOS, adicione um shebang no início do script para executá-lo diretamente:

#!/usr/bin/env kotlin

// meu-script.main.kts
println("Executando diretamente!")
println("Argumentos: ${args.joinToString()}")

Depois torne executável com chmod +x meu-script.main.kts e rode com ./meu-script.main.kts.

IDE support no IntelliJ

O IntelliJ IDEA reconhece arquivos .main.kts nativamente. Você ganha:

  • Autocomplete completo, incluindo dependências declaradas com @file:DependsOn
  • Highlighting de sintaxe e erros em tempo real
  • Botão de run direto no editor
  • Navegação para código-fonte das dependências

Isso é uma vantagem significativa sobre Bash, onde o suporte de IDE é limitado. Se você usa VS Code, o suporte a .main.kts é mais básico, mas funcional.

Cache e performance

A primeira execução de um script .main.kts é mais lenta porque o Kotlin precisa compilar e resolver dependências. Execuções subsequentes usam cache e são significativamente mais rápidas. Se performance de cold start é crítica, considere compilar o script para um JAR com kotlinc -script script.main.kts.

Para scripts que rodam em pipelines de CI/CD, o cache de dependências Maven ajuda bastante.

Limitações e futuro

Algumas limitações atuais do Kotlin Script:

  1. Cold start — a primeira execução compila o script, o que leva alguns segundos. Para scripts que rodam uma vez e somem (como em cron jobs), isso pode importar
  2. Sem suporte a Kotlin Multiplatform — scripts rodam apenas na JVM, não em Native ou WASM
  3. Debugging limitado — embora o IntelliJ suporte breakpoints em .main.kts, a experiência não é tão polida quanto em projetos estruturados
  4. kscript em manutenção — a ferramenta kscript, que adicionava features como cache agressivo e execução em background, não recebe atualizações desde 2023. A recomendação atual é usar .main.kts nativo ou JBang como alternativa

A JetBrains confirmou que continuará investindo em .main.kts como o formato oficial de scripting. Melhorias em IDE support e performance de cold start estão no roadmap para versões futuras do Kotlin.

Quando migrar um script para um projeto

Se seu script .main.kts começa a crescer além de 200-300 linhas, considere migrar para um projeto Kotlin estruturado. Sinais de que é hora de migrar:

  • Você precisa de múltiplos arquivos ou módulos
  • O script precisa de testes automatizados — veja nosso post sobre Power-Assert para testes sem dependências externas
  • Distribuição para outros devs que podem não ter o Kotlin instalado
  • Performance de startup importa e você quer compilar para um fat JAR

A migração é simples: o código Kotlin é o mesmo, você só precisa mover de top-level para dentro de funções e adicionar um build.gradle.kts. Toda a lógica, extension functions e imports continuam funcionando.

Perguntas frequentes

Posso usar coroutines em Kotlin Script?

Sim. Adicione @file:DependsOn("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2") e use coroutines normalmente. Você vai precisar de runBlocking como entry point, já que o script roda em contexto top-level.

Kotlin Script funciona no Windows?

Sim. Desde que o Kotlin esteja instalado e no PATH, funciona em Windows, Linux e macOS. O shebang (#!/usr/bin/env kotlin) funciona apenas em sistemas Unix.

Posso acessar argumentos da linha de comando?

Sim. A variável args (do tipo Array<String>) está disponível automaticamente em scripts .main.kts, similar ao args da função main() em projetos Kotlin convencionais.

Como usar Kotlin Script em pipelines de CI/CD?

Instale o Kotlin no runner de CI e execute com kotlin script.main.kts. Em GitHub Actions, você pode usar a action setup-kotlin para configurar o ambiente. Para Gitea Actions, o setup é similar.