在Scala中与外部进程交互(i / o)

时间:2015-05-07 13:47:29

标签: scala process io

我正在寻找一种简单的方法来启动外部进程,然后将字符串写入其输入并读取其输出。

在Python中,这有效:

mosesProcess = subprocess.Popen([mosesBinPath, '-f', mosesModelPath], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE);

# ...

mosesProcess.stdin.write(aRequest);
mosesAnswer = mosesProcess.stdout.readline().rstrip();

# ...

mosesProcess.stdin.write(anotherRequest);
mosesAnswer = mosesProcess.stdout.readline().rstrip();

# ...

mosesProcess.stdin.close();

我认为在Scala中应该使用 scala.sys.process.ProcessBuilder scala.sys.process.ProcessIO 来完成,但我不知道它们是如何工作的(尤其是后者)。

编辑:

我尝试过这样的事情:

val inputStream = new scala.concurrent.SyncVar[java.io.OutputStream];
val outputStream = new scala.concurrent.SyncVar[java.io.InputStream];
val errStream = new scala.concurrent.SyncVar[java.io.InputStream];

val cmd = "myProc";

val pb = process.Process(cmd);
val pio = new process.ProcessIO(stdin => inputStream.put(stdin),
  stdout => outputStream.put(stdout),
  stderr => errStream.put(stderr));

pb.run(pio);

inputStream.get.write(("request1" + "\n").getBytes);

println(outputStream.get.read); // It is blocked here

inputStream.get.write(("request2" + "\n").getBytes);

println(outputStream.get.read);

inputStream.get.close()

但是执行被卡住了。

3 个答案:

答案 0 :(得分:2)

当然,下面的attrib在写入方面不是一个很好的例子。我有一个输入/输出的EchoServer

    import scala.sys.process._
    import java.io._

    object EchoClient{
      def main(args: Array[String]) {
        var bContinue=true
        var cmd="C:\\\\windows\\system32\\attrib.exe"
        println(cmd)

        val process = Process (cmd)
        val io = new ProcessIO (
           writer,
           out => {scala.io.Source.fromInputStream(out).getLines.foreach(println)},
           err => {scala.io.Source.fromInputStream(err).getLines.foreach(println)})

        while (bContinue) {
          process run io
          var answer = readLine("Run again? (y/n)? ")
          if (answer=="n" || answer=="N")
            bContinue=false
        }
      }
      def reader(input: java.io.InputStream) = {
        // read here
      }

      def writer(output: java.io.OutputStream) = {
        // write here
        // 
      }

      // TODO: implement an error logger
    }

输出

C:\\windows\system32\attrib.exe
A            C:\dev\EchoClient$$anonfun$1.class
A            C:\dev\EchoClient$$anonfun$2$$anonfun$apply$1.class
A            C:\dev\EchoClient$$anonfun$2.class
A            C:\dev\EchoClient$$anonfun$3$$anonfun$apply$2.class
A            C:\dev\EchoClient$$anonfun$3.class
A            C:\dev\EchoClient$.class
A            C:\dev\EchoClient.class
A            C:\dev\EchoClient.scala
A            C:\dev\echoServer.bat
A            C:\dev\EchoServerChg$$anonfun$main$1.class
A            C:\dev\EchoServerChg$.class
A            C:\dev\EchoServerChg.class
A            C:\dev\EchoServerChg.scala
A            C:\dev\ScannerTest$$anonfun$main$1.class
A            C:\dev\ScannerTest$.class
A            C:\dev\ScannerTest.class
A            C:\dev\ScannerTest.scala
Run again? (y/n)?

答案 1 :(得分:1)

ProcessIO的Scala API:

new ProcessIO(in: (OutputStream) ⇒ Unit, out: (InputStream) ⇒ Unit, err: (InputStream) ⇒ Unit)

我想你应该提供至少两个参数,1个outputStream函数(写入进程),1个inputStream函数(从进程中读取)。

例如:

def readJob(in: InputStream) { 
    // do smthing with in 
}
def writeJob(out: OutputStream) { 
     // do somthing with out
}
def errJob(err: InputStream) { 
    // do smthing with err 
}
val process = new ProcessIO(writeJob, readJob, errJob)

请记住,流是Java流,因此您必须检查Java API。

编辑:package page提供示例,也许您可​​以查看它们。

答案 2 :(得分:1)

ProcessIO是进行低级别控制和输入输出交互的方法。甚至还有一个经常被忽视的辅助对象BasicIO,它有助于创建用于读取的通用ProcessIO实例,使用实用程序功能连接/输出流。您可以查看BasicIO.scala的源代码,了解它在创建ProcessIO实例时的内部操作。

有时,您可以从项目为类本身创建的测试用例或工具中找到灵感。对于Scala,请查看GitHub上的源代码。我们很幸运,有一个详细的例子,用于scala GraphViz Dot流程转换器DotRunner.scala