39

我正在使用 Scalascala.sys.process库。

我知道我可以捕获退出代码!和输出!!但是如果我想捕获两者怎么办?

我已经看到这个答案https://stackoverflow.com/a/6013932/416338看起来很有希望,但我想知道是否有一个班轮,我错过了一些东西。

4

7 回答 7

30

我有以下用于运行命令的实用程序方法:

import sys.process._
def runCommand(cmd: Seq[String]): (Int, String, String) = {
  val stdoutStream = new ByteArrayOutputStream
  val stderrStream = new ByteArrayOutputStream
  val stdoutWriter = new PrintWriter(stdoutStream)
  val stderrWriter = new PrintWriter(stderrStream)
  val exitValue = cmd.!(ProcessLogger(stdoutWriter.println, stderrWriter.println))
  stdoutWriter.close()
  stderrWriter.close()
  (exitValue, stdoutStream.toString, stderrStream.toString)
}

如您所见,它捕获标准输出、标准错误和结果代码。

于 2013-03-15T17:22:10.490 回答
19

您可以使用ProcessIO. 我在 Specs2 测试中需要类似的东西,我必须检查退出值以及进程的输出,具体取决于stdinin并且out是类型String)上的输入:

"the operation" should {
  f"return '$out' on input '$in'" in {
    var res = ""
    val io = new ProcessIO(
      stdin  => { stdin.write(in.getBytes)
                  stdin.close() }, 
      stdout => { res = convertStreamToString(stdout)
                  stdout.close() },
      stderr => { stderr.close() })
    val proc = f"$operation $file".run(io)
    proc.exitValue() must be_==(0)
    res must be_==(out)
  }
}

我想这可能会对你有所帮助。在示例中,我忽略了来自stderr.

于 2013-05-11T22:48:56.070 回答
12

您可以指定捕获文本的输出流:

import sys.process._
val os   = new java.io.ByteArrayOutputStream
val code = ("volname" #> os).!
os.close()
val opt  = if (code == 0) Some(os.toString("UTF-8")) else None
于 2014-12-29T14:06:09.837 回答
6

BasicIO 或 ProcessLogger 的单行使用很有吸引力。

scala> val sb = new StringBuffer
sb: StringBuffer = 

scala> ("/bin/ls /tmp" run BasicIO(false, sb, None)).exitValue
res0: Int = 0

scala> sb
res1: StringBuffer = ...

或者

scala> import collection.mutable.ListBuffer
import collection.mutable.ListBuffer

scala> val b = ListBuffer[String]()
b: scala.collection.mutable.ListBuffer[String] = ListBuffer()

scala> ("/bin/ls /tmp" run ProcessLogger(b append _)).exitValue
res4: Int = 0

scala> b mkString "\n"
res5: String = ...

根据捕获的含义,除非退出代码不为零,否则您可能对输出感兴趣。在这种情况下,处理异常。

scala> val re = "Nonzero exit value: (\\d+)".r.unanchored
re: scala.util.matching.UnanchoredRegex = Nonzero exit value: (\d+)

scala> Try ("./bomb.sh" !!) match {
     | case Failure(f) => f.getMessage match {
     |   case re(x) => println(s"Bad exit $x")
     | }
     | case Success(s) => println(s)
     | }
warning: there were 1 feature warning(s); re-run with -feature for details
Bad exit 3
于 2013-05-11T22:11:05.330 回答
2

除非性能较差,否则您的链接中“Alex Cruise”提供的响应相当简洁。

您可以扩展 sys.process.ProcessLogger 来管理

var out = List[String]()
var err = List[String]()

在内部,带有用于 out.reverse 和 err.reverse 结果的吸气剂。

于 2013-03-15T03:05:21.420 回答
0

这是一个非常简单的 Scala 包装器,它允许您检索标准输出、标准错误和退出代码。

import scala.sys.process._

case class ProcessInfo(stdout: String, stderr: String, exitCode: Int)

object CommandRunner {

def runCommandAndGetOutput(command: String): ProcessInfo = {
    val stdout = new StringBuilder
    val stderr = new StringBuilder
    val status = command ! ProcessLogger(stdout append _, stderr append _)
    ProcessInfo(stdout.toString(), stderr.toString(), status)
  }
}
于 2019-08-14T10:07:44.720 回答
0

我结合了这些并想出了这个。预期的 RC 在那里,因为我有一个程序需要在一个项目中运行,它在工作时返回 1。这确实取决于异常的文本,但它仍然会做一些不匹配的合理事情。

  private val ProcessErrorP: Regex = "(.*): error=(\\d+),(.*)".r.unanchored

  case class ProcessInfo(stdout: String, stderr: String, exitCode: Int, private val expectedRd: Int) {
    def succeeded: Boolean = exitCode == expectedRd
    def failed: Boolean = !succeeded
    def asOpt: Option[String] = if (succeeded) None else Some(stderr)
  }

  /**
   * Run a simple command
   * @param command -- what to run
   * @return -- what happened
   */
  def run(command: String, expectedRc: Int = 0): ProcessInfo = {
    try {
      val stdout = new StringBuilder
      val stderr = new StringBuilder
      val status = command ! ProcessLogger(stdout append _, stderr append _)
      ProcessInfo(stdout.toString(), stderr.toString(), status, expectedRc)
    } catch {
      case io: IOException =>
        val dm = io.getMessage
        dm match {
          case ProcessErrorP(message, code, reason) =>
            ProcessInfo("", s"$message, $reason", code.toInt, expectedRc)
          case m: String =>
            ProcessInfo("", m, 999, expectedRc)
        }
    }
  }
于 2021-09-27T18:26:15.257 回答