【问题标题】:How to issue a command that produces infinite output and return immediately如何发出产生无限输出并立即返回的命令
【发布时间】:2018-02-12 02:03:31
【问题描述】:

当我编写以下代码时(用菊石但我认为这并不重要)

("tail -f toTail.txt" lineStream) foreach(println(_)),程序按预期给了我最后一行但随后挂起,即使我在文件中写了更多内容,也没有任何结果。

API 如何支持无限输出的进程?

我试着写val myStream = ("tail -f toTail.txt" lineStream_!) 但它仍然没有返回 write away

这是 scala 文档所说的:

lineStream: 像 run 一样立即返回,生成的输出通过 Stream[String] 提供。获取该 Stream 的下一个元素可能会阻塞,直到它可用为止。

所以我不明白为什么它会阻塞

顺便说一句,我使用 Ammonite API 的行为完全相同

如果再次输入%%("tail", "-f", "toTail.txt"),该方法会挂起并且不会立即返回。

【问题讨论】:

  • 描述“运行方式不正确”是什么意思
  • 我期待一个 inputStream 被返回。但它不会返回它只是在那里阻塞。我可能对流类的工作方式有点困惑。对于要返回的流对象,数据应该是有限的而不是无限的?
  • 据我了解,流是计算机懒惰的。所以这应该不是问题docs.scala-lang.org/overviews/collections/…
  • 问题可能来自 Scala Process Builder 方法 lineStream
  • 我认为,这个问题有点不清楚(实际上没有什么“挂起”,也没有什么应该“立即返回”),但问题确实存在。澄清一下:"tail -f foo.txt".lineStream.foreach(println) 预计不会“立即返回”,它应该坐在那里,并在添加到foo.txt 时打印出更多行。它做第一件事(坐在那里),而不是另一件事 - 没有额外的行被打印出来,因为它们在命令启动后被添加到文件中。

标签: scala ammonite


【解决方案1】:

ProcessBuilder 没有问题(至少不是源于您的用例)。来自ProcessBuilder documentation

启动进程

要执行与 ProcessBuilder 相关的所有外部命令,可以使用四组方法之一。这些方法中的每一种都具有各种重载和变化,以实现对 I/O 的进一步控制。这些方法是:

  • run:最通用的方法,立即返回一个scala.sys.process.Process,外部命令并发执行。
  • !:阻塞直到所有外部命令退出,并返回执行链中最后一个的退出代码。
  • !!:阻塞,直到所有外部命令退出,并返回一个带有生成输出的字符串。
  • lineStream:像run一样立即返回,生成的输出通过Stream[String]提供。 获取该 Stream 的下一个元素可能会阻塞,直到它可用为止。如果返回码不为零,此方法将引发异常——如果不需要,请使用 lineStream_!方法。

文档明确指出lineStream 可能会阻塞,直到下一行可用。由于tail -f 的本质是无限行lineBreak,程序将阻塞等待下一行出现。

对于以下假设我有一个文件:/Users/user/tmp/sample.txt,它的内容是:

boom
bar
cat

为什么lineStream_! 没有错

import scala.language.postfixOps
import scala.sys.process._

object ProcessBuilder extends App {

  val myStream: Stream[String] = ("tail /Users/user/tmp/sample.txt" lineStream_!)
  println("I'm after tail!")
  myStream.filter(_ != null).foreach(println)
  println("Finished")
  System.exit(0)

}

输出:

I'm after tail!
boom
bar
cat
Finished

所以您看到lineStream_! 立即返回。因为命令的性质是有限的。

如何从产生无限输出的命令中立即返回:

让我们用tail -f 试试这个。您需要对流程进行更多控制。同样,正如文档所述:

如果希望完全控制输入和输出,那么 scala.sys.process.ProcessIO 可以与 run 一起使用。

举个例子:

import java.io.{BufferedReader, InputStreamReader}

import scala.language.postfixOps
import scala.sys.process._

object ProcessBuilder extends App {

  var reader: BufferedReader = _
  try {

    var myStream: Stream[String] = Stream.empty
    val processIO = new ProcessIO(
      (os: java.io.OutputStream) => ??? /* Send things to the process here */,
      (in: java.io.InputStream) => {

        reader = new BufferedReader(new InputStreamReader(in))
        myStream = Stream.continually(reader.readLine()).takeWhile(_ != "ff")
      },
      (in: java.io.InputStream) => ???,
      true
    )
    "tail -f /Users/user/tmp/sample.txt".run(processIO)
    println("I'm after the tail command...")

    Thread.sleep(2000)
    println("Such computation performed while tail was active!")

    Thread.sleep(2000)
    println("Such computation performed while tail was active again!")

    println(
      s"Captured these lines while computing: ${myStream.print(System.lineSeparator())}")

    Thread.sleep(2000)
    println("Another computation!")

  } finally {
    Option(reader).foreach(_.close())
  }
  println("Finished")
  System.exit(0)

}

输出:

I'm after the tail command...
Such computation performed while tail was active!
Such computation performed while tail was active again!
boom
bar
cat

它仍然会立即返回,现在它只是挂在那里等待更多输入。如果我从tmp 目录执行echo 'fff' >> sample.txt,程序输出:

Another computation!
Finished

您现在可以在发出tail -f 命令后执行您想要的任何计算,并且可以根据您传递给takeWhile 方法(或关闭输入流的其他方法)的条件来终止它。

有关ProcessIO 的更多详细信息,请查看documentation here

【讨论】:

  • 我认为,您回答了一个错误的问题:操作的问题似乎是 "tail -f foo.txt".lineStream.foreach(println) 没有打印出在命令启动后添加到 foo.txt 的新行。这确实令人费解……
  • “因此我不明白它为什么会阻塞” - 我回答了它为什么会阻塞。并提供了替代方案。也许他看到这个时可以澄清一下:)
  • @Dima "tail -f foo.txt".lineStream.foreach(println) 会打印发送到文件的行。 :)
  • 取决于您将它们发送到文件的方式(也可能在您的操作系统上)。看我的回答...
  • 也许吧。我用echo>> 运算符发送它们。运行El capitan 10.11.6
【解决方案2】:

我认为,这与您如何向文件中添加数据有关,而不是与ProcessBuilder 有关。例如,如果您在编辑器中使用它来添加数据,则每次保存时它都会使用不同的 inode 重写整个文件,而tail 没有检测到这一点。

尝试使用tail -F 而不是tail -f,应该可以。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-27
    • 2011-12-11
    • 2011-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多