2017-01-08 16 views
4

我开始并运行Scala进程。如何写一个字符串到Scala进程?

val dir = "/path/to/working/dir/" 
    val stockfish = Process(Seq("wine", dir + "stockfish_8_x32.exe")) 
    val logger = ProcessLogger(printf("Stdout: %s%n", _)) 
    val stockfishProcess = stockfish.run(logger, connectInput = true) 

该进程读取和写入标准IO(控制台)。如何将字符串命令发送到进程,如果它已经启动?

Scala过程API有ProcessBuilder,它反过来一堆有用的方法。但是在之前使用ProcessBuilder 一个进程开始编写复杂的shell命令。另外Scala还有ProcessIO来处理输入或输出。我也不需要它。我只需要发送消息给我的过程。

在Java中,我会做这样的事情。

 String dir = "/path/to/working/dir/"; 
     ProcessBuilder builder = new ProcessBuilder("wine", dir + "stockfish_8_x32.exe"); 
     Process process = builder.start(); 

     OutputStream stdin = process.getOutputStream(); 
     InputStream stdout = process.getInputStream(); 

     BufferedReader reader = new BufferedReader(new InputStreamReader(stdout)); 
     BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin)); 

     new Thread(() -> { 
      try { 
       String line; 
       while ((line = reader.readLine()) != null) { 
        System.out.println("Stdout: " + line); 
       } 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
     }).start(); 

     Thread.sleep(5000); // it's just for example 
     writer.write("quit"); // send to the process command to stop working 
     writer.newLine(); 
     writer.flush(); 

它工作得很好。我开始我的流程,从中获取InputStream和OutputStream,并使用流与流程进行交互。

看来Scala Process trait没有办法写入它。 ProcessBuilder在流程运行后没有用处。而ProcessIO仅用于IO捕获和处理。

有什么方法可以写入Scala运行过程吗?

UPDATE:

我不明白我怎么可以使用ProcessIO将字符串传递到正在运行的进程。 我做了以下。

import scala.io.Source 
import scala.sys.process._ 

object Sample extends App { 

    def out = (output: java.io.OutputStream) => { 
     output.flush() 
     output.close() 
    } 

    def in = (input: java.io.InputStream) => { 
     println("Stdout: " + Source.fromInputStream(input).mkString) 
     input.close() 
    } 

    def go = { 
     val dir = "/path/to/working/dir/" 
     val stockfishSeq = Seq("wine", dir + "/stockfish_8_x32.exe") 
     val pio = new ProcessIO(out, in, err => {}) 
     val stockfish = Process(stockfishSeq) 
     stockfish.run(pio) 

     Thread.sleep(5000) 
     System.out.write("quit\n".getBytes) 
     pio.writeInput(System.out) // "writeInput" is function "out" which I have passed to conforming ProcessIO instance. I can invoke it from here. It takes OutputStream but where can I obtain it? Here I just pass System.out for example. 
    } 
    go 
} 

当然,它不工作,我不明白如何实现功能,如我在上面的Java代码片段。如果有建议或者Scala代码片段清除我的问题,那将是非常好的。

+0

我想我误解了你的问题,所以我删除了我的回答。你应该指定你正在使用哪个版本的Scala(我认为最近改变了与子流程相关的API)。我很困惑为什么ProcessIO不适合你。看起来你的Java代码只是使用stdin/stdout,这对ProcessIO来说也是可能的。你能澄清吗? – DaoWen

+0

@DaoWen是的,Java中很简单。 Java“Process”类提供了返回可以使用的IO流的方法。 Scala“Process”特征只有三种方法:destroy,exitValue和isAlive。 ProcessIO类具有可直接传递和调用的函数参数“writeInput”。我猜这只是一种对输入事件的倾听,但是如何创建这个“事件”呢?我使用最后一个Scala版本 - 2.12.1。 –

回答

0

我没有真正尝试过这个,但documentation表示您可以使用ProcessIO的实例来处理Process的输入和输出,其方式与您在Java中执行的操作类似。

1

我认为围绕Scala过程的文档(特别是ProcessIO的使用和语义)可以使用一些改进。我第一次尝试使用这个API时,我也发现它很混乱,并且需要一些试验和错误才能让我的子进程I/O正常工作。

我认为看到一个简单的例子可能是你真正需要的。我会做一些非常简单的事情:调用bc作为一个子进程来做一些简单的计算,然后将答案打印到我的stdout。我的目标是做这样的事情(但是从Scala中而不是从我的shell):

$ printf "1+2\n3+4\n" | bc 
3 
7 

这是我怎么会做它用Scala:

import scala.io.Source 
import scala.sys.process._ 

object SimpleProcessExample extends App { 

    def out = (output: java.io.OutputStream) => { 
    output.flush() 
    output.close() 
    } 

    def in = (input: java.io.InputStream) => { 
    println("Stdout: " + Source.fromInputStream(input).mkString) 
    input.close() 
    } 

    // limit scope of any temporary variables 
    locally { 
    val calcCommand = "bc" 
    // strings are implicitly converted to ProcessBuilder 
    // via scala.sys.process.ProcessImplicits.stringToProcess(_) 
    val calcProc = calcCommand.run(new ProcessIO(
     // Handle subprocess's stdin 
     // (which we write via an OutputStream) 
     in => { 
     val writer = new java.io.PrintWriter(in) 
     writer.println("1 + 2") 
     writer.println("3 + 4") 
     writer.close() 
     }, 
     // Handle subprocess's stdout 
     // (which we read via an InputStream) 
     out => { 
     val src = scala.io.Source.fromInputStream(out) 
     for (line <- src.getLines()) { 
      println("Answer: " + line) 
     } 
     src.close() 
     }, 
     // We don't want to use stderr, so just close it. 
     _.close() 
    )) 

    // Using ProcessBuilder.run() will automatically launch 
    // a new thread for the input/output routines passed to ProcessIO. 
    // We just need to wait for it to finish. 

    val code = calcProc.exitValue() 

    println(s"Subprocess exited with code $code.") 

    } 
} 

注意,你实际上并不直接调用ProcessIO对象的任何方法,因为它们被ProcessBuilder自动调用。

这里的结果:

$ scala SimpleProcessExample 
Answer: 3 
Answer: 7 
Subprocess exited with code 0. 

如果输入和输出处理程序的子进程之间要互动,你可以使用标准的线程通讯工具(例如,有在附近过的BlockingQueue一个实例)。

+1

谢谢你的例子。很说明。然而,在你的代码片段中,你知道在创建过程的时候你到底想要计算什么,并且将所有逻辑移动到“in”方法都没有问题。 –

+1

想象一下,您的计算器正在等待用户输入一些计算器应该处理的表达式。 在“in”函数中没有什么可写的。你不知道什么是用户类型。 如果您有OutputStream实例,就像在Java中一样,只要用户键入内容,就可以随时写入该实例。但你没有。 这是我的问题。我可以开始处理,但在工作期间无法写入。 –

0

下面是一个过程,其中可以写入和从该过程开始之后读获得的输入和输出流的示例:

object demo { 
    import scala.sys.process._ 

    def getIO = { 
    // create piped streams that can attach to process streams: 
    val procInput = new java.io.PipedOutputStream() 
    val procOutput = new java.io.PipedInputStream() 
    val io = new ProcessIO(
     // attach to the process's internal input stream 
     { in => 
     val istream = new java.io.PipedInputStream(procInput) 
     val buf = Array.fill(100)(0.toByte) 
     var br = 0 
     while (br >= 0) { 
      br = istream.read(buf) 
      if (br > 0) { in.write(buf, 0, br) } 
     } 
     in.close() 
     }, 
     // attach to the process's internal output stream 
     { out => 
     val ostream = new java.io.PipedOutputStream(procOutput) 
     val buf = Array.fill(100)(0.toByte) 
     var br = 0 
     while (br >= 0) { 
      br = out.read(buf) 
      if (br > 0) { ostream.write(buf, 0, br) } 
     } 
     out.close() 
     }, 
     // ignore stderr 
     { err =>() } 
    ) 
    // run the command with the IO object: 
    val cmd = List("awk", "{ print $1 + $2 }") 
    val proc = cmd.run(io) 

    // wrap the raw streams in formatted IO objects: 
    val procO = new java.io.BufferedReader(new java.io.InputStreamReader(procOutput)) 
    val procI = new java.io.PrintWriter(procInput, true) 
    (procI, procO) 
    } 
} 

下面是使用的输入和输出对象的一个​​简短的例子。请注意,这是很难保证的过程中会得到它的输入直到关闭输入流/对象,因为一切都是通过管道输送,缓冲等

scala> :load /home/eje/scala/input2proc.scala 
Loading /home/eje/scala/input2proc.scala... 
defined module demo 

scala> val (procI, procO) = demo.getIO 
procI: java.io.PrintWriter = [email protected] 
procO: java.io.BufferedReader = [email protected] 

scala> procI.println("1 2") 

scala> procI.println("3 4") 

scala> procI.println("5 6") 

scala> procI.close() 

scala> procO.readLine 
res4: String = 3 

scala> procO.readLine 
res5: String = 7 

scala> procO.readLine 
res6: String = 11 

scala> 

一般来说,如果您管理输入和输出的同时在同一个线程中,存在死锁的可能性,因为读取或写入都会阻止等待另一个。在他们自己的线程中运行输入逻辑和输出逻辑是最安全的。考虑到这些线程问题,也可以将输入和输出逻辑直接放入定义{ in => ... }{ out => ... },因为这些都是自动运行在单独的线程中

相关问题