8

からの関数として扱いたい外部プロセスがありますString=>String。入力の行が与えられると、それは出力の単一の行で応答します。私はscala.sys.processを使用する必要があるようです。これは明らかに、scala内から多くのシェル操作に簡単にアクセスできるエレガントなライブラリです。ただし、この単純なユースケースを実行する方法がわかりません。

プロセスのstdinに1行を書き込むと、結果が1行で出力されます。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重要な初期化ステップがあるため、への呼び出しごとにプロセスがリロードされないことが重要です。

4

5 に答える 5

3

だから-私のコメントの後-これは私の解決策になるでしょう

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」(単純な文字列=>ユニット関数)に渡されます。メインは単なるサンプル実装です-pythonを呼び出します;)

于 2013-02-11T20:18:00.377 に答える
2

よくわかりませんが、そのようなものが欲しいですか?

  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

結果 :

 1    foo

 2    bar

 3    baz

 4    buz

 5    puz

PlayCLIの方が良い方法だと思います。

于 2013-02-06T18:12:10.550 に答える
1

http://blog.greweb.fr/2013/01/playcli-play-iteratees-unix-pipe/今日これに出くわし、あなたが望むものとまったく同じように見えます

于 2013-01-30T18:09:20.883 に答える
1

Akka俳優を使ってみませんか。アクターは状態を持つことができ、したがって(スレッド内の)開いているプログラムへの参照を持つことができます。そのアクターにメッセージを送信できます。

ProcessWrapperは、型指定されたアクター自体、または関数の呼び出しをアクターの呼び出しに変換するものである可能性があります。メソッド名として「process」しかない場合は、wrapper ! "message"それで十分です。

プログラムを開いてコマンドを受信する準備をすることは、メッセージを受信するアクターのように聞こえます。

于 2013-02-08T07:32:24.650 に答える
0

編集:おそらく私は要件を間違えました。同じプロセスに複数の行を送信したい。以下の解決策ではそれは不可能です。

1つの可能性は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))
}

これで、次のような方法を使用できます。

scala> ("bc":ProcessBuilder).#<<("3 + 4\n").!!
res9: String = 
"7
"

String2つの変換( -> ProcessBuilder-> )が必要でProcessBuilderWithStringInputあり、Scalaは自動的に1つの変換のみを適用するため、型注釈が必要であることに注意してください。

于 2013-01-30T07:37:24.027 に答える