Shell脚本终端可运行,Kotlin调用无法启动MapTiler服务器
问题排查:Kotlin调用MapTiler启动脚本卡住的原因与解决方法
核心原因
通过Java/Kotlin的Runtime.exec()或kscript的runCommand()调用长期运行的进程(如MapTiler服务器)时,子进程的标准输出(stdout)和标准错误(stderr)会被系统缓冲区缓存。当缓冲区被填满后,子进程会暂停执行,等待父进程读取缓冲区内容,最终导致出现CPU占用为0的阻塞状态。而直接在终端运行脚本时,终端会实时消费这些输出,不会触发缓冲区阻塞。
验证依据
- 执行
ls/date等输出量极小的命令时正常,因为输出不会填满缓冲区 - 手动终止bash进程后,Kotlin应用抛出异常,说明父进程一直等待子进程退出,但子进程实际因缓冲区阻塞无法继续运行
解决方案
方案1:修改脚本,重定向输出
在setup_server.sh中把输出重定向到文件或/dev/null,避免缓冲区堆积:
#! /bin/sh ./maptiler-server --workDir "./" --adminPassword "abcdefg" > server.log 2>&1
若不需要保留日志,可直接丢弃输出:
#! /bin/sh ./maptiler-server --workDir "./" --adminPassword "abcdefg" > /dev/null 2>&1
方案2:在Kotlin代码中主动处理子进程输出
通过单独线程读取子进程的stdout和stderr,避免缓冲区阻塞:
class ScriptExecutor { companion object{ @JvmStatic fun execute(s: String){ try { // 简化命令,去掉冗余的sh嵌套 val cmd = arrayOf("/bin/sh", "./maptiler-server-macos/setup_server.sh") val process = Runtime.getRuntime().exec(cmd) // 启动线程读取标准输出 Thread { process.inputStream.bufferedReader().useLines { lines -> lines.forEach { println(it) } } }.start() // 启动线程读取标准错误 Thread { process.errorStream.bufferedReader().useLines { lines -> lines.forEach { System.err.println(it) } } }.start() // 若无需等待服务器启动完成,可注释下面的代码 // process.waitFor() }catch (e: Exception){ e.printStackTrace() } } } }
额外优化:简化调用命令
原命令/bin/sh -c sh ./xxx.sh存在冗余嵌套,直接使用/bin/sh ./maptiler-server-macos/setup_server.sh即可;也可以给脚本添加可执行权限(chmod +x setup_server.sh),之后直接调用./maptiler-server-macos/setup_server.sh。
内容的提问来源于stack exchange,提问作者Chicovara
相关产品推荐
相关产品推荐

