Как выполнить команду, которая производит бесконечный вывод и возвращает сразу
Когда я пишу следующий код (в аммоните, но я не думаю, что это имеет значение)
("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 ответа
С ProcessBuilder не возникает проблем (по крайней мере, это не связано с вашим вариантом использования). Из документации ProcessBuilder:
Стартовые процессы
Для выполнения всех внешних команд, связанных с ProcessBuilder, можно использовать одну из четырех групп методов. Каждый из этих методов имеет различные перегрузки и вариации для обеспечения дополнительного контроля над вводом / выводом. Эти методы:
- run: самый общий метод, он немедленно возвращает scala.sys.process.Process, и внешняя команда выполняется одновременно.
- !: блокирует до тех пор, пока не завершатся все внешние команды, и не вернет код выхода последней в цепочке выполнения.
- !!: блокирует до тех пор, пока не завершатся все внешние команды, и возвращает строку с созданным выводом.
- 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
проверьте документацию здесь.
Я думаю, это связано с тем, как вы добавляете данные в файл, а не с ProcessBuilder
, Если вы используете его в редакторе, например, для добавления данных, он перезаписывает весь файл каждый раз, когда вы сохраняете, с другим индексом и tail
не обнаруживает это.
Попробуй сделать tail -F
вместо tail -f
, это должно работать.