Kotlin脚本自动化任务应用案例
Kotlin 脚本基础
Kotlin 脚本是一种允许你直接运行 Kotlin 代码而无需将其编译成完整 Java 类的方式。它为快速原型开发、自动化任务等场景提供了极大的便利。
在 Kotlin 脚本中,代码结构相对简洁。例如,一个简单的 Kotlin 脚本用于打印 “Hello, World!” 可能如下:
println("Hello, World!")
你可以将上述代码保存为 hello.kts
文件,然后在命令行中通过 kotlinc -script hello.kts
来运行它。
脚本依赖管理
在实际应用中,我们的脚本可能需要依赖外部库。Kotlin 脚本支持使用 @file:DependsOn
注解来声明依赖。例如,如果我们想要在脚本中使用 OkHttp
库来进行 HTTP 请求:
@file:DependsOn("com.squareup.okhttp3:okhttp:4.9.1")
import okhttp3.OkHttpClient
import okhttp3.Request
val client = OkHttpClient()
val request = Request.Builder()
.url("https://www.example.com")
.build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException("Unexpected code $response")
println(response.body?.string())
}
这里通过 @file:DependsOn
引入了 OkHttp
库的特定版本,使得我们可以在脚本中使用 OkHttp
提供的功能。
自动化文件处理任务
批量重命名文件
假设我们有一个目录,里面包含大量图片文件,命名格式不太规范,我们希望将它们批量重命名为统一格式,例如 image_001.jpg
, image_002.jpg
等。
首先,我们需要获取目录下的所有文件,然后对每个文件进行重命名操作。以下是实现该功能的 Kotlin 脚本:
import java.io.File
fun main() {
val directory = File("/path/to/your/images")
var counter = 1
directory.listFiles()?.forEach { file ->
if (file.isFile && file.name.endsWith(".jpg") || file.name.endsWith(".png")) {
val newFileName = "image_${"%03d".format(counter)}${file.extension}"
val newFile = File(directory, newFileName)
file.renameTo(newFile)
counter++
}
}
}
在上述代码中,我们定义了目标目录 directory
。然后通过 listFiles
获取目录下的所有文件。对于每个满足图片格式(.jpg
或 .png
)的文件,我们生成一个新的文件名,使用 renameTo
方法将文件重命名。
筛选特定文件并复制
有时我们需要在一个复杂的目录结构中筛选出特定类型的文件,并将它们复制到另一个目录。比如,我们要从一个项目目录中筛选出所有的 *.kt
文件,并将它们复制到一个备份目录。
import java.io.File
import java.nio.file.Files
import java.nio.file.StandardCopyOption
fun main() {
val sourceDirectory = File("/path/to/your/project")
val targetDirectory = File("/path/to/backup")
targetDirectory.mkdirs()
sourceDirectory.walkTopDown()
.filter { it.isFile && it.name.endsWith(".kt") }
.forEach { file ->
val relativePath = sourceDirectory.toPath().relativize(file.toPath())
val targetFile = File(targetDirectory, relativePath.toString())
targetFile.parentFile?.mkdirs()
Files.copy(file.toPath(), targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
}
}
这里我们使用 walkTopDown
方法遍历源目录及其子目录。筛选出所有的 Kotlin 源文件(.kt
)。对于每个筛选出的文件,我们计算其相对于源目录的相对路径,并在目标目录中创建相应的路径结构,最后使用 Files.copy
方法将文件复制过去。
自动化构建与部署任务
基于 Kotlin 脚本的 Gradle 辅助构建
Gradle 是一个强大的构建工具,而 Kotlin 脚本可以很好地与之配合。假设我们有一个多模块的 Kotlin 项目,我们希望通过一个 Kotlin 脚本来自动化执行一些 Gradle 任务,比如编译、测试和打包。
首先,我们可以在项目根目录下创建一个 build_helpers.kts
脚本:
import org.gradle.tooling.*
fun executeGradleTask(taskName: String) {
val builder = GradleConnector.newConnector()
.forProjectDirectory(File("."))
val connection = builder.connect()
try {
connection.newBuild()
.forTasks(taskName)
.run()
} finally {
connection.close()
}
}
fun main() {
executeGradleTask("clean")
executeGradleTask("build")
executeGradleTask("test")
}
在上述脚本中,我们使用 GradleConnector
连接到当前项目的 Gradle 构建系统。通过 executeGradleTask
函数,我们可以执行任意的 Gradle 任务。在 main
函数中,我们依次执行了 clean
、build
和 test
任务,实现了项目的清理、构建和测试自动化。
自动化部署到服务器
假设我们已经完成了项目的构建,生成了可部署的工件(如 JAR 文件),现在我们要将其部署到远程服务器。我们可以使用 SSH 协议来实现这一过程。
首先,我们需要添加 SSH 相关的依赖到我们的 Kotlin 脚本:
@file:DependsOn("com.jcraft:jsch:0.1.55")
import com.jcraft.jsch.*
fun main() {
val user = "your_username"
val host = "your_server_host"
val password = "your_password"
val localFilePath = "/path/to/your/build/output.jar"
val remoteFilePath = "/path/to/deploy/on/server/output.jar"
val jsch = JSch()
val session: Session = jsch.getSession(user, host, 22)
session.setPassword(password)
val config = java.util.Properties()
config.put("StrictHostKeyChecking", "no")
session.setConfig(config)
session.connect()
val sftpChannel = session.openChannel("sftp") as ChannelSftp
sftpChannel.connect()
sftpChannel.put(localFilePath, remoteFilePath)
sftpChannel.disconnect()
session.disconnect()
}
在这段代码中,我们使用 JSch
库来建立 SSH 连接。通过 Session
进行身份验证并连接到远程服务器。然后打开一个 SFTP 通道,将本地构建生成的 JAR 文件上传到远程服务器的指定路径,完成自动化部署的一部分工作。
自动化测试任务
单元测试自动化执行
在 Kotlin 项目中,我们通常使用 JUnit 或其他测试框架来编写单元测试。我们可以编写一个 Kotlin 脚本来自动化执行这些单元测试。
假设我们的项目使用 JUnit 5,并且测试类位于 src/test/kotlin
目录下。以下是一个自动化执行单元测试的 Kotlin 脚本:
import org.junit.platform.launcher.*
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder
import org.junit.platform.launcher.core.LauncherFactory
import org.junit.platform.launcher.listeners.SummaryGeneratingListener
import java.net.URI
fun main() {
val testClassPath = URI.create("file://${System.getProperty("user.dir")}/build/classes/kotlin/test")
val request = LauncherDiscoveryRequestBuilder.request()
.selectors(Selectors.selectPackage("com.example.yourpackage"))
.classpath(testClassPath)
.build()
val launcher: Launcher = LauncherFactory.create()
val listener = SummaryGeneratingListener()
launcher.execute(request, listener)
val summary = listener.summary()
println("Tests executed: ${summary.totalTestsCount}")
println("Tests failed: ${summary.failedTestCount}")
}
在上述脚本中,我们首先构建了一个 LauncherDiscoveryRequest
,指定要测试的包(com.example.yourpackage
)以及测试类所在的类路径。然后创建一个 Launcher
并执行测试请求,通过 SummaryGeneratingListener
来收集测试结果并打印出执行的测试总数和失败的测试数。
集成测试自动化流程
集成测试通常涉及多个组件之间的交互,并且可能需要启动一些服务。例如,我们有一个基于 Spring Boot 的微服务项目,并且使用 Testcontainers 来进行集成测试。
我们可以编写一个 Kotlin 脚本来自动化整个集成测试流程,包括启动数据库容器、启动微服务应用和执行集成测试。
@file:DependsOn("org.testcontainers:testcontainers:1.16.3")
@file:DependsOn("org.testcontainers:postgresql:1.16.3")
@file:DependsOn("org.springframework.boot:spring-boot-starter-test:2.6.7")
import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.boot.test.context.SpringBootTest
import org.testcontainers.containers.PostgreSQLContainer
import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers
import java.util.concurrent.TimeUnit
@Testcontainers
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class IntegrationTest {
@Container
val postgresContainer: PostgreSQLContainer<*> = PostgreSQLContainer("postgres:14.1")
.withDatabaseName("test_db")
.withUsername("test_user")
.withPassword("test_password")
companion object {
@JvmStatic
fun main(args: Array<String>) {
postgresContainer.start()
val hikariConfig = HikariConfig()
hikariConfig.jdbcUrl = postgresContainer.jdbcUrl
hikariConfig.username = postgresContainer.username
hikariConfig.password = postgresContainer.password
val dataSource = HikariDataSource(hikariConfig)
val app = SpringApplicationBuilder(YourSpringBootApplication::class.java)
.properties("spring.datasource.url" to postgresContainer.jdbcUrl,
"spring.datasource.username" to postgresContainer.username,
"spring.datasource.password" to postgresContainer.password)
.run(*args)
// 等待应用启动完成
TimeUnit.SECONDS.sleep(5)
// 执行集成测试,可以通过反射调用测试类的方法
// 这里假设存在一个 IntegrationTestClass 类包含集成测试方法
val testClass = Class.forName("com.example.IntegrationTestClass")
val testMethod = testClass.getDeclaredMethod("integrationTestMethod")
testMethod.invoke(testClass.newInstance())
app.close()
postgresContainer.stop()
}
}
}
在这个脚本中,我们使用 Testcontainers
启动了一个 PostgreSQL 数据库容器。然后通过 SpringApplicationBuilder
启动 Spring Boot 应用,并配置其连接到刚刚启动的数据库容器。等待应用启动后,通过反射调用集成测试方法,最后关闭应用和数据库容器。
自动化数据处理任务
从 CSV 文件读取并处理数据
CSV(逗号分隔值)文件是一种常见的数据存储格式。我们可以编写 Kotlin 脚本来读取 CSV 文件中的数据,并进行一些处理,比如计算平均值、筛选特定数据等。
假设我们有一个 data.csv
文件,内容如下:
name,age
Alice,25
Bob,30
Charlie,28
以下是读取该 CSV 文件并计算平均年龄的 Kotlin 脚本:
import java.io.File
fun main() {
val file = File("data.csv")
var totalAge = 0
var count = 0
file.bufferedReader().useLines { lines ->
lines.drop(1).forEach { line ->
val parts = line.split(',')
val age = parts[1].toInt()
totalAge += age
count++
}
}
val averageAge = if (count > 0) totalAge / count.toFloat() else 0f
println("Average age: $averageAge")
}
在上述代码中,我们使用 bufferedReader
逐行读取文件内容。通过 drop(1)
跳过 CSV 文件的表头行。然后对每一行数据进行拆分,提取年龄字段并累加到 totalAge
中,同时记录数据行数 count
。最后计算平均年龄并打印出来。
数据转换并写入新文件
有时我们需要对读取到的数据进行转换,然后将转换后的数据写入到新的文件中。例如,我们从一个 JSON 文件中读取数据,将其中的日期格式进行转换,并写入到另一个 JSON 文件。
假设我们有一个 input.json
文件,内容如下:
[
{
"name": "Alice",
"birthDate": "2023-01-01"
},
{
"name": "Bob",
"birthDate": "2023-02-01"
}
]
我们希望将 birthDate
格式从 yyyy - MM - dd
转换为 MM/dd/yyyy
,并写入 output.json
。以下是实现该功能的 Kotlin 脚本:
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
fun main() {
val inputFile = File("input.json")
val outputFile = File("output.json")
val gson = Gson()
val dateFormat = SimpleDateFormat("yyyy - MM - dd")
val newDateFormat = SimpleDateFormat("MM/dd/yyyy")
val data = gson.fromJson(inputFile.readText(), Array<Map<String, String>>::class.java)
val transformedData = data.map { item ->
val birthDate = dateFormat.parse(item["birthDate"]!!)
val newBirthDate = newDateFormat.format(birthDate)
mapOf("name" to item["name"], "birthDate" to newBirthDate)
}
val newGson = GsonBuilder().setPrettyPrinting().create()
outputFile.writeText(newGson.toJson(transformedData))
}
在这个脚本中,我们首先使用 Gson
库读取 input.json
文件中的数据,并将其转换为 Array<Map<String, String>>
类型。然后对每个数据项中的 birthDate
进行日期格式转换。最后,使用 GsonBuilder
将转换后的数据以漂亮的格式写入 output.json
文件。
自动化系统监控任务
监控系统资源使用情况
我们可以使用 Kotlin 脚本来监控系统的资源使用情况,如 CPU 使用率、内存使用率等。在 Java 中,我们可以通过 ManagementFactory
来获取这些信息,Kotlin 同样可以利用这一点。
以下是一个监控 CPU 和内存使用率的 Kotlin 脚本:
import com.sun.management.OperatingSystemMXBean
import java.lang.management.ManagementFactory
fun getCpuUsage(): Double {
val osBean = ManagementFactory.getOperatingSystemMXBean() as OperatingSystemMXBean
val startTime = System.nanoTime()
val startCpuTime = osBean.processCpuTime
Thread.sleep(100)
val endTime = System.nanoTime()
val endCpuTime = osBean.processCpuTime
val cpuUsage = (endCpuTime - startCpuTime).toDouble() / (endTime - startTime)
return cpuUsage
}
fun getMemoryUsage(): Double {
val runtime = Runtime.getRuntime()
val usedMemory = runtime.totalMemory() - runtime.freeMemory()
val totalMemory = runtime.totalMemory()
return usedMemory.toDouble() / totalMemory * 100
}
fun main() {
while (true) {
val cpuUsage = getCpuUsage()
val memoryUsage = getMemoryUsage()
println("CPU Usage: ${cpuUsage * 100}%")
println("Memory Usage: $memoryUsage%")
Thread.sleep(5000)
}
}
在上述代码中,getCpuUsage
函数通过记录一段时间内的 CPU 时间和系统时间来计算 CPU 使用率。getMemoryUsage
函数通过 Runtime
获取已使用内存和总内存,从而计算出内存使用率。在 main
函数中,我们每隔 5 秒打印一次 CPU 和内存使用率。
监控文件系统变化
有时我们需要监控文件系统中特定目录的变化,比如文件的创建、修改或删除。在 Kotlin 中,我们可以使用 Java 的 WatchService
来实现这一功能。
以下是一个监控指定目录文件变化的 Kotlin 脚本:
import java.nio.file.*
import java.nio.file.attribute.BasicFileAttributes
import java.util.concurrent.TimeUnit
fun main() {
val watchService = FileSystems.getDefault().newWatchService()
val directoryToWatch = Paths.get("/path/to/watch")
directoryToWatch.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE)
while (true) {
val watchKey = watchService.poll(1, TimeUnit.SECONDS)
if (watchKey == null) continue
watchKey.pollEvents().forEach { event ->
val kind = event.kind()
if (kind == StandardWatchEventKinds.OVERFLOW) return@forEach
val affectedPath = event.context() as Path
println("Event kind: $kind, Affected path: $affectedPath")
}
watchKey.reset()
}
}
在这个脚本中,我们创建了一个 WatchService
,并将指定目录注册到该服务中,监听文件的创建、修改和删除事件。在循环中,我们不断轮询 WatchService
获取事件,并打印出事件类型和受影响的文件路径。
通过以上各种 Kotlin 脚本自动化任务的应用案例,我们可以看到 Kotlin 脚本在不同领域的强大功能和灵活性,能够帮助我们提高工作效率,实现各种复杂任务的自动化。无论是文件处理、构建部署、测试还是数据和系统监控,Kotlin 脚本都提供了简洁且有效的解决方案。在实际应用中,我们可以根据具体需求进一步优化和扩展这些脚本,以满足更复杂的业务场景。