2

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

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

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

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

这是 scala 文档所说的:

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

因此我不明白为什么它会阻塞

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

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

4

2 回答 2

3

ProcessBuilder 没有问题(至少不是源于您的用例)。从ProcessBuilder 文档

启动进程

要执行与 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

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

Another computation!
Finished

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

有关在此处ProcessIO查看文档的更多详细信息。

于 2017-09-04T00:12:08.847 回答
1

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

尝试做tail -F而不是tail -f,这应该工作。

于 2017-09-04T12:34:35.753 回答