Scala sys.process将进程包装为函数
我有一个外部流程,我想将其视为 函数来自Scala sys.process将进程包装为函数,scala,Scala,我有一个外部流程,我想将其视为 函数来自String=>String。给定一行输入,它将以一行输出响应。看来我应该用 scala.sys.process,这显然是一个优雅的库,使许多 可以从scala中轻松访问shell操作。然而,我 无法理解如何执行这个简单的用例 如果我向进程的stdin写入一行,它将打印结果 在一行中。如何使用sys.process创建包装器,以便 可以交互地使用流程吗?例如,如果我有一个 ProcessWrapper的实现,下面是一个程序及其输出: // abstract
String=>String
。给定一行输入,它将以一行输出响应。看来我应该用
scala.sys.process,这显然是一个优雅的库,使许多
可以从scala中轻松访问shell操作。然而,我
无法理解如何执行这个简单的用例
如果我向进程的stdin写入一行,它将打印结果
在一行中。如何使用sys.process
创建包装器,以便
可以交互地使用流程吗?例如,如果我有一个
ProcessWrapper
的实现,下面是一个程序及其输出:
// abstract definition
class ProcessWrapper(executable: String) {
def apply(line: String): String
}
// program using an implementation
val process = new ProcessWrapper("cat -b")
println(process("foo"))
println(process("bar"))
println(process("baz"))
输出:
1 foo
2 bar
3 baz
重要的是,每次调用
process
都不要重新加载该流程,因为有一个重要的初始化步骤。Edit:可能是我把需求搞错了。您希望向同一进程发送多行。用下面的解决方案是不可能的
一种可能是向ProcessBuilder
添加一个扩展方法,该方法允许从字符串获取输入:
implicit class ProcessBuilderWithStringInput(val builder: ProcessBuilder) extends AnyVal {
// TODO: could use an implicit for the character set
def #<<(s: String) = builder.#<(new ByteArrayInputStream(s.getBytes))
}
隐式类ProcessBuilderWithStringInput(val builder:ProcessBuilder)扩展AnyVal{
//TODO:可以对角色集使用隐式
def#今天遇到了这个,看起来和你想要的一模一样我不确定,但你想要这样的东西吗
case class ProcessWrapper(executable: String) {
import java.io.ByteArrayOutputStream
import scala.concurrent.duration.Duration
import java.util.concurrent.TimeUnit
lazy val process = sys.runtime.exec(executable)
def apply(line: String, blockedRead: Boolean = true): String = {
process.getOutputStream().write(line.getBytes())
process.getOutputStream().flush()
val r = new ByteArrayOutputStream
if (blockedRead) {
r.write(process.getInputStream().read())
}
while (process.getInputStream().available() > 0) {
r.write(process.getInputStream().read())
}
r.toString()
}
def close() = process.destroy()
}
val process = ProcessWrapper("cat -b")
println(process("foo\n"))
println(process("bar\n"))
println(process("baz\n"))
println(process("buz\n"))
println(process("puz\n"))
process.close
结果:
我认为这是一个更好的方法。使用Akka actor怎么样。actor可以有状态,因此可以引用一个打开的程序(在线程中)。您可以向该actor发送消息
ProcessWrapper可能是类型化的参与者本身,也可能只是将函数调用转换为参与者调用的东西。如果方法名只有“process”,那么wrapper!”消息“
就足够了
打开一个程序并准备好接收命令听起来像是一个接收消息的演员。所以,在我的评论之后,这将是我的解决方案
import java.io.BufferedReader
import java.io.File
import java.io.InputStream
import java.io.InputStreamReader
import scala.annotation.tailrec
class ProcessWrapper(cmdLine: String, lineListenerOut: String => Unit, lineListenerErr: String => Unit,
finishHandler: => Unit,
lineMode: Boolean = true, envp: Array[String] = null, dir: File = null) {
class StreamRunnable(val stream: InputStream, listener: String => Unit) extends Runnable {
def run() {
try {
val in = new BufferedReader(new InputStreamReader(this.stream));
@tailrec
def readLines {
val line = in.readLine
if (line != null) {
listener(line)
readLines
}
}
readLines
}
finally {
this.stream.close
finishHandler
}
}
}
val process = Runtime.getRuntime().exec(cmdLine, envp, dir);
val outThread = new Thread(new StreamRunnable(process.getInputStream, lineListenerOut), "StreamHandlerOut")
val errThread = new Thread(new StreamRunnable(process.getErrorStream, lineListenerErr), "StreamHandlerErr")
val sendToProcess = process.getOutputStream
outThread.start
errThread.start
def apply(txt: String) {
sendToProcess.write(txt.getBytes)
if (lineMode)
sendToProcess.write('\n')
sendToProcess.flush
}
}
object ProcessWrapper {
def main(args: Array[String]) {
val process = new ProcessWrapper("python -i", txt => println("py> " + txt),
err => System.err.println("py err> " + err), System.exit(0))
while (true) {
process(readLine)
}
}
}
主要部分是StreamRunnable,其中进程在线程中读取,接收到的行传递给“LineListener”(一个简单的字符串=>Unit-function)。
main只是一个示例实现-调用python;)是的,我确实想向同一个进程发送多行代码。在第一次加载进程时,有一个重要的初始化步骤。但是,我没有一次将所有数据发送到该进程。它看起来像是ProcessIO
之上的一个层,以简化管道。I希望我想要的是一个简单的用例,但对我的问题缺乏回应让我重新考虑。是的,我正在寻找类似的东西。理想情况下,我会使用scala可执行库,但不清楚如何做到这一点。PlayCLI越来越像我想要的。我会仔细看看。我希望我描述的类方法ribed,我不知道如何使用PlayCLI实现这一点,因为它涉及到将变异与迭代对象混合。为什么要使用ProcessIO?您会在actor中使用Java还是Scala接口来处理流程?我可能会使用Scala接口我明白您的意思,但这实际上只适用于cat-b
我想-pRabLee是,你的应用程序被同步调用,应该返回一个字符串。现在考虑一个命令,它返回多行,并说一秒钟执行。如何在完成处理和收集足够的行时知道应用程序?应该用一个简单的行侦听器来考虑一个异步的解决方案。(从流程中获取每一行)或者使用Akka actors-并重新表述您的问题。同步解决方案将不起作用-除非您总是得到一个单行调用apply。唉,我认为由于不规则刷新,对于每个输入都输出单行响应的命令也会出现问题。我重写了scala的一些java代码-现在使用侦听器。请参阅如果您喜欢:)。我在java中仍然有一个更完善的方法,甚至支持进程优先级-但后者仅适用于windows。将其移植到Scala和Linux/OSX会非常有趣。实际上,while(true)
和exit
实际上只是一个示例。当远程执行另一个命令时,您通常会在输出处理程序中进行处理,并在内部识别进程何时完成(不调用exit)。
1 foo
2 bar
3 baz
4 buz
5 puz
import java.io.BufferedReader
import java.io.File
import java.io.InputStream
import java.io.InputStreamReader
import scala.annotation.tailrec
class ProcessWrapper(cmdLine: String, lineListenerOut: String => Unit, lineListenerErr: String => Unit,
finishHandler: => Unit,
lineMode: Boolean = true, envp: Array[String] = null, dir: File = null) {
class StreamRunnable(val stream: InputStream, listener: String => Unit) extends Runnable {
def run() {
try {
val in = new BufferedReader(new InputStreamReader(this.stream));
@tailrec
def readLines {
val line = in.readLine
if (line != null) {
listener(line)
readLines
}
}
readLines
}
finally {
this.stream.close
finishHandler
}
}
}
val process = Runtime.getRuntime().exec(cmdLine, envp, dir);
val outThread = new Thread(new StreamRunnable(process.getInputStream, lineListenerOut), "StreamHandlerOut")
val errThread = new Thread(new StreamRunnable(process.getErrorStream, lineListenerErr), "StreamHandlerErr")
val sendToProcess = process.getOutputStream
outThread.start
errThread.start
def apply(txt: String) {
sendToProcess.write(txt.getBytes)
if (lineMode)
sendToProcess.write('\n')
sendToProcess.flush
}
}
object ProcessWrapper {
def main(args: Array[String]) {
val process = new ProcessWrapper("python -i", txt => println("py> " + txt),
err => System.err.println("py err> " + err), System.exit(0))
while (true) {
process(readLine)
}
}
}