如何发出产生无限输出并立即返回的命令

时间:2017-09-03 18:48:24

标签: scala ammonite

当我写下面的代码时(在菊石中,但我不认为这很重要)

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

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

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

以下是scala doc所说的内容:

  

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

因此,我不明白为什么会阻止

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

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

2 个答案:

答案 0 :(得分:1)

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

  

启动流程

     

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

     
      
  • run:最通用的方法,它立即返回一个scala.sys.process.Process,并且外部命令同时执行。
  •   
  • !:阻塞,直到所有外部命令退出,并返回执行链中最后一个的退出代码。
  •   
  • !!:阻塞,直到所有外部命令退出,并返回一个生成输出的String。
  •   
  • lineStream:像run一样立即返回,生成的输出通过Stream [String]提供。 获取该流的下一个元素可能会阻止,直到可用。如果返回码不为零,此方法将抛出异常 - 如果不需要,请使用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。

举个例子:

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.txt目录执行tmp,则程序输出:

Another computation!
Finished

现在,您可以在发出tail -f命令后执行所需的任何计算,并根据传递给takeWhile方法的条件(或关闭输入流)。

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

答案 1 :(得分:1)

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

尝试执行tail -F代替tail -f,这应该有用。