标准方法是使用ProcessBuilder,并启动另一个线程来读取进程的标准输出(这是您的输入)。
我不确定这是否是最好的方法,但这里有一些我想出的代码来做一些非常相似的事情。 (在我的例子中,进程写入了它的 stdout 和 stderr,所以我需要同时阅读它们——但在进程完成之前我不需要查看它们。我还需要返回进程的退出状态以及这两个状态, 并处理超时。不过,我不需要向进程的标准输入发送任何内容;如果需要,则必须对其进行扩展。)
/**
* Runs a system command and captures its output (and stderr).
*
* @param command The program name and any arguments.
* @param workingDir The working directory of the process, or `null` for the same as this process.
* @param timeoutSecs Maximum time to wait for it to finish, in seconds. (Default is 5 mins.)
* @param throwOnFailure Whether to throw a [ProcessFailedException] if it returns a non-zero exit value.
* @throws IndexOutOfBoundsException if the command is empty.
* @throws SecurityException if a security manager prevented creation of the process or a redirection.
* @throws UnsupportedOperationException if the OS doesn't support process creation.
* @throws IOException if an I/O error occurs.
* @throws ProcessTimedOutException if the timeout expires before the process finishes.
* @throws ProcessFailedException if the process returns a non-zero exit status.
*/
fun runProcess(vararg command: String, workingDir: File? = null, timeoutSecs: Long = 300,
throwOnFailure: Boolean = true): ProcessResult {
val proc = ProcessBuilder(*command)
.directory(workingDir)
.start()
// Must read both output and error, else it can deadlock.
class StreamReader(val stream: InputStream, val result: StringBuilder) : Runnable {
override fun run() = stream.bufferedReader().use { s ->
while (true)
result.appendln(s.readLine() ?: break)
}
}
val output = StringBuilder()
Thread(StreamReader(proc.inputStream, output)).start()
val error = StringBuilder()
Thread(StreamReader(proc.errorStream, error)).start()
val exited = proc.waitFor(timeoutSecs, TimeUnit.SECONDS)
if (!exited)
throw ProcessTimedOutException("${command[0]} timed out!", timeoutSecs, output.toString(), error.toString())
val exitValue = proc.exitValue()
if (exitValue != 0 && throwOnFailure)
throw ProcessFailedException("${command[0]} failed: $error", exitValue, output.toString(), error.toString())
return ProcessResult(exitValue, output.toString(), error.toString())
}
/** Thrown if a process doesn't finish within the timeout period. */
class ProcessTimedOutException(msg: String, val timeoutSecs: Long, val output: String, val error: String) : Exception(msg)
/** Thrown if a process returns a non-zero code. */
class ProcessFailedException(msg: String, val exitValue: Int, val output: String, val error: String) : Exception(msg)
/** Returned if a process completes. */
class ProcessResult(val exitValue: Int, val output: String, val error: String)