2017-01-08 19 views
4

Я запускаю и запускаю процесс Scala.Как написать строку Scala Process?

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 (консоль). Как я могу отправить строковую команду процессу, если он уже запущен?

API-интерфейс Scala имеет ProcessBuilder, который имеет в свою очередь множество полезных методов. Но ProcessBuilder используется до, процесс начинает составлять сложные команды оболочки. Кроме того, 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, которая не позволяет писать. 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» предоставляет методы, возвращающие потоки ввода-вывода, которые я могу использовать. Скала «Процесс» имеет только три метода: destroy, exitValue и isAlive. Класс ProcessIO имеет функциональный параметр «writeInput», который может быть передан и вызван впоследствии напрямую. Как я думаю, это просто слушатель для входных событий, но как создать это «событие» ?. Я использую последнюю версию Scala - 2.12.1. –

ответ

0

Я на самом деле не пробовал это, но documentation говорит, что вы можете использовать экземпляр ProcessIO для обработки ввода и вывода процесса способом, аналогичным тому, что вы делали бы на Java.

1

Я думаю, что документация вокруг процессов Scala (в частности, использование и семантика ProcessIO) может использовать некоторое улучшение. В первый раз, когда я попытался использовать этот API, я также счел его очень запутанным, и для правильной работы моего подпроцесса i/o потребовалось несколько проб и ошибок.

Я думаю, что увидеть простой пример - это, вероятно, все, что вам действительно нужно. Я сделаю что-то очень простое: вызывая bc как подпроцесс для выполнения некоторых тривиальных вычислений, а затем для печати ответов на мой stdout. Моя цель состоит в том, чтобы сделать что-то вроде этого (но от Scala, а не из моей оболочки):

$ 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 => ... }, так как они оба запускаются в отдельных потоках автоматически

 Смежные вопросы

  • Нет связанных вопросов^_^