如何以交互方式使用Scala流程?

时间:2018-02-17 08:35:15

标签: scala process io

我在Scala中为一个使用文本输入和输出的游戏编写机器人。所以我希望以交互方式处理一个进程 - 也就是说,我的代码接收进程的输出,使用它,然后才将其下一个输入发送到进程。所以我想同时给一个函数访问inputStreams和outputStream。

这似乎不适合scala.sys.process.BasicIO中的任何工厂或scala.sys.process.ProcessIO的构造函数(三个函数,每个函数只能访问一个流)

这是我现在的表现。

private var rogue_input: OutputStream = _
private var rogue_output: InputStream = _
private var rogue_error: InputStream = _

Process("python3 /home/robin/IdeaProjects/Rogomatic/python/rogue.py --rogomatic").run(
  new ProcessIO(rogue_input = _, rogue_output = _, rogue_error = _)
)

try {
  private val rogue_scanner = new Scanner(rogue_output)
  private val rogue_writer = new PrintWriter(rogue_input, true)

  // Play the game
} finally {
  rogue_input.close()
  rogue_output.close()
  rogue_error.close()
}

这很有效,但它并不像Scala那样。有没有更惯用的方法呢?

1 个答案:

答案 0 :(得分:1)

  

所以我想以交互方式处理一个进程 - 也就是说,我的代码接收进程的输出,使用它,然后才将其下一个输入发送到进程。

一般来说,这传统上由expect解决。存在受expect启发的各种语言的库和工具,包括Scala:https://github.com/Lasering/scala-expect

项目的自述文件提供了各种示例。虽然我不确切知道rouge.py / stdin互动对stdout}的期望,但这里有一个快速的“hello world”示例,展示了如何与Python解释器进行交互(使用Ammonite REPL,具有方便的库导入功能):

import $ivy.`work.martins.simon::scala-expect:6.0.0`

import work.martins.simon.expect.core._
import work.martins.simon.expect.core.actions._

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._

val timeout = 5 seconds

val e = new Expect("python3 -i -", defaultValue = "?")(
    new ExpectBlock(
      new StringWhen(">>> ")(
        Sendln("""print("hello, world")""")
      )
    ),
    new ExpectBlock(
      new RegexWhen("""(.*)\n>>> """.r)(
        ReturningWithRegex(_.group(1).toString)
      )
    )
  )

e.run(timeout).onComplete(println)

上述代码的作用是“预期”>>>被发送到stdout,当它发现时,它会发送print("hello, world"),然后是换行符。从那时起,它会使用正则表达式读取并返回所有内容,直到下一个提示符(>>>)。

在其他调试信息中,上述内容应导致Success(hello, world)打印到您的控制台。

图书馆有各种其他风格,也可能还有其他类似的图书馆。我的主要观点是,expect灵感的图书馆很可能是您正在寻找的。