1. 使用 ProcessBuilder

Kotlin 作为 JVM 上的语言,天然支持 Java 的标准类库,我们可以通过 ProcessBuilder 类来调用外部命令。这是目前推荐的方式之一,因为它提供了更灵活、可控的 API。

我们来看一个简单的例子,执行 java -version 命令并验证执行结果:

@Test
fun `Given a command, When executed with ProcessBuilder, Then it is executed successfully`() {
    val result = ProcessBuilder("java", "-version")
      .redirectOutput(ProcessBuilder.Redirect.INHERIT)
      .redirectError(ProcessBuilder.Redirect.INHERIT)
      .start()
      .waitFor()
    assertThat(result).isEqualTo(0)
}

✅ 说明:

  • 使用 ProcessBuilder 构造命令参数时,建议使用多个字符串参数,避免命令注入等潜在问题
  • redirectOutput(...)redirectError(...) 设置为 INHERIT 表示输出和错误信息会直接输出到当前进程的控制台,方便调试
  • start() 启动进程,waitFor() 等待执行完成并返回退出码(0 表示成功)

⚠️ 踩坑提示:
在 Windows 上执行命令时,可能需要显式调用 cmd /c 来执行脚本,例如:

ProcessBuilder("cmd.exe", "/c", "echo", "Hello World")

2. 使用 Runtime

虽然 Runtime 类也可以执行外部命令,但其 API 相对简单,功能也有限。

@Test
fun `Given a command, When executed with Runtime, Then it is executed successfully`() {
    val process = Runtime.getRuntime().exec("java -version")
    val result = process.waitFor()
    assertThat(result).isEqualTo(0)
}

⚠️ 踩坑提示:

  • Runtime.exec(String) 方法从 JDK 18 开始被标记为 废弃(deprecated)
  • 建议改用 exec(String[])exec(String... command) 形式,避免命令解析错误或安全问题

✅ 推荐写法:

val process = Runtime.getRuntime().exec(arrayOf("java", "-version"))

❌ 不推荐写法(已废弃):

val process = Runtime.getRuntime().exec("java -version") // 不推荐

3. 小结与建议

方法 是否推荐 特点说明
ProcessBuilder ✅ 推荐 更灵活、支持重定向、跨平台兼容性好
Runtime.exec() ❌ 不推荐 API 简单、功能有限,且已被废弃

总结:

  • 如果你需要执行外部命令并控制其输入输出、环境变量等行为,强烈建议使用 ProcessBuilder
  • 避免使用 Runtime.exec(String),因为它不仅被废弃,还容易因命令解析不当导致安全问题或运行时异常

完整示例代码可参考:GitHub 示例仓库


原始标题:How to Invoke External Command From Within Kotlin