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 Kotlin — groupBy, 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:
- 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
- Sem suporte a Kotlin Multiplatform — scripts rodam apenas na JVM, não em Native ou WASM
- Debugging limitado — embora o IntelliJ suporte breakpoints em
.main.kts, a experiência não é tão polida quanto em projetos estruturados - 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.ktsnativo 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.