Почему Kotlin — идеальный язык для CLI-утилит?
Когда речь заходит о создании консольных инструментов, многие разработчики по привычке тянутся к Python или Bash. Но если вы уже знакомы с Kotlin (а на нашем сайте pythonlib.ru мы уверены, что хороший разработчик владеет несколькими языками), то знаете, насколько этот язык лаконичен и выразителен. Создание CLI утилит на Kotlin — это не просто дань моде, а реальный способ получить быстрые, типобезопасные и кроссплатформенные инструменты.
Представьте: вы пишете консольное приложение на Kotlin, которое компилируется в JAR или нативный бинарник через GraalVM или Kotlin/Native. Оно запускается везде — от Linux-сервера до Windows-машины коллеги. При этом код получается в 2-3 раза короче, чем на Java, и намного безопаснее, чем на Python. Звучит заманчиво? Давайте разбираться.
Что нужно для старта?
Для создания Kotlin CLI вам понадобится минимум:
- Установленный JDK 11+ (рекомендую 17 или 21)
- IntelliJ IDEA (Community версии достаточно) или любой текстовый редактор
- Базовое знание Kotlin (функции, классы, коллекции)
- Gradle или Maven для сборки
Если вы еще не работали с Kotlin в консоли — не страшно. Мы пройдем путь от пустого файла до полноценной утилиты, которую можно опубликовать и использовать в CI/CD пайплайнах.
Шаг 1: Настройка проекта для CLI утилиты
Создадим простой Gradle-проект. Вот минимальный build.gradle.kts:
plugins {
kotlin("jvm") version "1.9.22"
application
}
application {
mainClass.set("com.pythonlib.cli.MainKt")
}
repositories {
mavenCentral()
}
dependencies {
implementation("com.github.ajalt.clikt:clikt:4.2.2")
implementation("org.jetbrains.kotlinx:kotlinx-cli:0.3.6")
}
Обратите внимание на две ключевые библиотеки: Clikt и Kotlinx CLI. Первая — самая популярная библиотека для создания CLI утилит на Kotlin. Вторая — официальное решение от JetBrains. В этой статье мы будем использовать Clikt, так как он дает больше гибкости и красивее выглядит.
Шаг 2: Пишем первую команду
Создадим файл src/main/kotlin/com/pythonlib/cli/Main.kt:
package com.pythonlib.cli
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.options.flag
class GreetCommand : CliktCommand(
name = "greet",
help = "Приветствует пользователя"
) {
private val name: String by argument(
help = "Имя пользователя"
)
private val uppercase: Boolean by option(
"--uppercase", "-u",
help = "Вывести имя в верхнем регистре"
).flag()
override fun run() {
val greeting = "Привет, $name!"
if (uppercase) {
echo(greeting.uppercase())
} else {
echo(greeting)
}
}
}
fun main(args: Array<String>) {
GreetCommand().main(args)
}
Это простейшая Kotlin CLI утилита, которая принимает имя и опциональный флаг. Запустим:
./gradlew run --args="Алексей --uppercase"
# Вывод: ПРИВЕТ, АЛЕКСЕЙ!
Всего 30 строк кода — и у нас работающий консольный инструмент. Clikt сам генерирует справку, обрабатывает ошибки и поддерживает автодополнение для bash/zsh.
Шаг 3: Многоуровневые команды (как в Git)
Настоящие консольные приложения на Kotlin редко состоят из одной команды. Давайте сделаем утилиту для работы с файлами, которая поддерживает подкоманды:
import com.github.ajalt.clikt.core.subcommands
class FileTool : CliktCommand(
name = "filetool",
help = "Утилита для работы с файлами"
) {
override fun run() {
echo("Используйте подкоманды: count, search")
}
}
class CountCommand : CliktCommand(
name = "count",
help = "Подсчитывает строки в файле"
) {
private val file: String by argument(help = "Путь к файлу")
override fun run() {
val lines = java.io.File(file).readLines().size
echo("Количество строк: $lines")
}
}
class SearchCommand : CliktCommand(
name = "search",
help = "Ищет текст в файле"
) {
private val pattern: String by argument(help = "Искомый текст")
private val file: String by argument(help = "Путь к файлу")
override fun run() {
val results = java.io.File(file).readLines()
.filter { it.contains(pattern, ignoreCase = true) }
echo("Найдено ${results.size} совпадений:")
results.forEach { echo(it) }
}
}
fun main(args: Array<String>) {
FileTool().subcommands(CountCommand(), SearchCommand()).main(args)
}
Теперь наша CLI утилита на Kotlin ведет себя как настоящий профессиональный инструмент:
./gradlew run --args="count data.txt"
./gradlew run --args="search error app.log"
Шаг 4: Продвинутые фишки Clikt
Библиотека Clikt предлагает множество возможностей, которые делают создание CLI утилит на Kotlin по-настоящему приятным. Вот несколько примеров:
Автоматическая валидация входных данных
import com.github.ajalt.clikt.parameters.types.int
import com.github.ajalt.clikt.parameters.types.file
class ValidateCommand : CliktCommand() {
private val port: Int by option("--port", "-p")
.int()
.check("Порт должен быть от 1024 до 65535") { it in 1024..65535 }
private val config: java.io.File by option("--config", "-c")
.file(mustExist = true, canBeDir = false)
override fun run() {
echo("Порт: $port, конфиг: ${config.absolutePath}")
}
}
Интерактивный режим и прогресс-бары
import com.github.ajalt.clikt.output.TermUI
import kotlinx.coroutines.delay
class ProgressCommand : CliktCommand() {
override fun run() {
echo("Начинаем загрузку...")
TermUI.progress("Обработка файлов") {
for (i in 1..100) {
delay(50)
this.progress = i.toDouble() / 100
}
}
echo("Готово!")
}
}
Шаг 5: Сборка нативного бинарника
Чтобы ваша Kotlin CLI утилита работала без установленной JVM