Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala sys.process将进程包装为函数_Scala - Fatal编程技术网

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)
    }
  }
}