Scala:从 ProcessBuilder 获取 Scala Stream+Process 实例
Scala: Get Scala Stream+Process instance from ProcessBuilder
我有以下选择:
选项 #1:lineStream(log: ProcessLogger): Stream[String]
(以及 3 个类似选项)
选项 #2:run(log: ProcessLogger): Process
(以及 4 个类似选项)
我怎样才能同时获得,Stream[String]
+ Process
?
如果那不可能,当我决定使用第一个选项(Stream[String]
但没有 Process
实例)时,我如何提前销毁系统进程?
这是我不喜欢 Scala 中的过程 API 的原因之一——您通常可以选择其中之一,但不能同时选择两者。我认为 API 无法满足您的需求。如果您查看 ProcessBuilderImpl.lineStream()
实现,流程引用仅存储在本地并且无法访问它。
这是一个简单的示例,您可以根据 lineStream()
的实际编写方式(参见 ProcessBuilderImpl
和 BasicIO
)自己实现此类功能。我没有时间完善它(比如 return 比元组更好的东西),但它应该给你一个想法。
object StreamProcessLogger {
private val nonzeroException = true // set it to whatever suits you
def run(processBuilder: ProcessBuilder): (Process, Stream[String]) = {
val logger = new StreamProcessLogger
val process = processBuilder.run(logger)
waitForExitInAnotherThread(process, logger)
(process, logger.stream)
}
private def waitForExitInAnotherThread(process: Process, logger: StreamProcessLogger) = {
val thread = new Thread() {
override def run() = { logger.setExitCode(process.exitValue()) }
}
thread.start()
}
}
private class StreamProcessLogger extends ProcessLogger {
val queue = new LinkedBlockingQueue[Either[Int, String]]
override def buffer[T](f: => T): T = f
override def out(s: => String): Unit = queue.put(Right(s))
override def err(s: => String): Unit = queue.put(Right(s))
def stream = next()
def setExitCode(exitCode: Int) = queue.put(Left(exitCode))
private def next(): Stream[String] = queue.take match {
case Left(0) => Stream.empty
case Left(code) => if (StreamProcessLogger.nonzeroException) scala.sys.error("Nonzero exit code: " + code) else Stream.empty
case Right(s) => Stream.cons(s, next())
}
}
这是用法:
test("returns stream and process") {
val (process, stream) = StreamProcessLogger.run(Process("ls"))
stream.foreach(println)
println(process.exitValue())
}
我有以下选择:
选项 #1:lineStream(log: ProcessLogger): Stream[String]
(以及 3 个类似选项)
选项 #2:run(log: ProcessLogger): Process
(以及 4 个类似选项)
我怎样才能同时获得,Stream[String]
+ Process
?
如果那不可能,当我决定使用第一个选项(Stream[String]
但没有 Process
实例)时,我如何提前销毁系统进程?
这是我不喜欢 Scala 中的过程 API 的原因之一——您通常可以选择其中之一,但不能同时选择两者。我认为 API 无法满足您的需求。如果您查看 ProcessBuilderImpl.lineStream()
实现,流程引用仅存储在本地并且无法访问它。
这是一个简单的示例,您可以根据 lineStream()
的实际编写方式(参见 ProcessBuilderImpl
和 BasicIO
)自己实现此类功能。我没有时间完善它(比如 return 比元组更好的东西),但它应该给你一个想法。
object StreamProcessLogger {
private val nonzeroException = true // set it to whatever suits you
def run(processBuilder: ProcessBuilder): (Process, Stream[String]) = {
val logger = new StreamProcessLogger
val process = processBuilder.run(logger)
waitForExitInAnotherThread(process, logger)
(process, logger.stream)
}
private def waitForExitInAnotherThread(process: Process, logger: StreamProcessLogger) = {
val thread = new Thread() {
override def run() = { logger.setExitCode(process.exitValue()) }
}
thread.start()
}
}
private class StreamProcessLogger extends ProcessLogger {
val queue = new LinkedBlockingQueue[Either[Int, String]]
override def buffer[T](f: => T): T = f
override def out(s: => String): Unit = queue.put(Right(s))
override def err(s: => String): Unit = queue.put(Right(s))
def stream = next()
def setExitCode(exitCode: Int) = queue.put(Left(exitCode))
private def next(): Stream[String] = queue.take match {
case Left(0) => Stream.empty
case Left(code) => if (StreamProcessLogger.nonzeroException) scala.sys.error("Nonzero exit code: " + code) else Stream.empty
case Right(s) => Stream.cons(s, next())
}
}
这是用法:
test("returns stream and process") {
val (process, stream) = StreamProcessLogger.run(Process("ls"))
stream.foreach(println)
println(process.exitValue())
}