Пакет scala.sys.process
пакет process
Члены типа
класс FileProcessLogger extends ProcessLogger with Closeable with Flushable
трайт Process extends AnyRef
Представляет процесс, который выполняется или завершил выполнение. Может быть составным процессом с несколькими базовыми процессами (например, a #&& b).
Этот трайт часто не используется напрямую, хотя его компаньон-объект содержит фабрики для scala.sys.process.ProcessBuilder, основного компонента этого пакета.
Он используется напрямую при вызове метода run на ProcessBuilder, что запускает процесс в фоновом режиме. Методы, предоставляемые на Process, позволяют заблокировать выполнение, пока процесс не завершится, и получить код завершения, или уничтожить процесс.
- См. также
трайт ProcessBuilder extends Source with Sink
Представляет последовательность одного или нескольких внешних процессов, которые могут быть выполнены. ProcessBuilder может быть одним внешним процессом или комбинацией других ProcessBuilder. Можно управлять тем, куда будет направлен вывод внешнего процесса и откуда будет поступать входной поток, или предоставить это решение тому, кто его запускает.
Создаётся ProcessBuilder через фабрики, предоставленные в компаньон-объекте scala.sys.process.Process, или неявные преобразования на основе этих фабрик, доступные в объекте пакета scala.sys.process. Вот несколько примеров:
import scala.sys.process._
// Executes "ls" and sends output to stdout
"ls".!
// Execute "ls" and assign a `LazyList[String]` of its output to "contents".
val contents = Process("ls").lazyLines
// Here we use a `Seq` to make the parameter whitespace-safe
def contentsOf(dir: String): String = Seq("ls", dir).!!
Методы ProcessBuilder разделены на три категории: те, которые объединяют два ProcessBuilder для создания третьего, те, которые перенаправляют входные или выходные данные ProcessBuilder, и те, которые выполняют внешние процессы, связанные с ним.
Объединение ProcessBuilder
Два существующих ProcessBuilder могут быть объединены следующим образом:
-
Они могут выполняться параллельно, при этом вывод первого используется как входной поток для второго, как в Unix-трубах. Это достигается методом
#|.Они могут выполняться последовательно, при этом второй запускается сразу после завершения первого. Это выполняется методом ###.Выполнение второго может быть обусловлено кодом возврата (статусом выхода) первого, либо только если он равен нулю, либо только если он не равен нулю. Методы #&& и #|| выполняют эти задачи.Перенаправление входных/выходных данных
Хотя управление входом и выходом можно выполнить при выполнении процесса, есть несколько методов, которые создают новый ProcessBuilder с предварительно настроенным входом или выходом. Это #<, #> и #>>, и они могут принимать в качестве входных данных другой ProcessBuilder (например, трубу, описанную выше), или что-то другое, например, java.io.File или java.io.InputStream. Например:
new URL("http://databinder.net/dispatch/About") #> "grep JSON" #>> new File("About_JSON") !
Запуск процессов
Для выполнения всех внешних команд, связанных с ProcessBuilder, можно использовать одну из четырёх групп методов. Каждый из этих методов имеет различные перегрузки и варианты для дополнительного управления вводом/выводом. Эти методы:
run: наиболее общий метод, он возвращает scala.sys.process.Process немедленно, и внешняя команда выполняется асинхронно.
!: ожидает, пока все внешние команды завершатся, и возвращает код выхода последней в цепочке выполнения.
!!: ожидает, пока все внешние команды завершатся, и возвращает String с сгенерированным выводом.
lazyLines: возвращает немедленно, как run, а генерируемый вывод предоставляется через LazyList[String]. Получение следующего элемента этого LazyList может заблокировать выполнение, пока он не станет доступным. Этот метод сгенерирует исключение, если код возврата отличается от нуля — если этого не нужно, используйте метод lazyLines_!.Обработка входных и выходных данных
Если не указано иначе, входные данные внешних команд, выполняемых с помощью run или !, не будут связаны ни с чем, а вывод будет перенаправлен на stdout и stderr процесса Scala. Для методов !! и lazyLines не будет предоставлен вход, а вывод будет перенаправлен в соответствии с семантикой этих методов.
Некоторые методы будут использовать stdin в качестве входных данных. Вывод можно управлять с помощью scala.sys.process.ProcessLogger — !! и lazyLines будут перенаправлять только вывод ошибок, если им будет передан ProcessLogger. Если необходимо полное управление вводом и выводом, то scala.sys.process.ProcessIO можно использовать с run.
Например, мы могли бы отключить вывод ошибок от lazyLines_! так:
val etcFiles = "find /etc" lazyLines_! ProcessLogger(line => ())
Расширенный пример
Давайте подробно рассмотрим один пример использования:
import scala.sys.process._
"find src -name *.scala -exec grep null {} ;" #| "xargs test -z" #&& "echo null-free" #|| "echo null detected" !
Обратите внимание, что каждый String неявно преобразуется в ProcessBuilder через неявные преобразования, импортированные из scala.sys.process. Эти ProcessBuilder затем объединяются тремя различными способами.
#| перенаправляет вывод первой команды на входную строку второй команды. Это отражает оболочку трубы (|).
#&& условно выполняет вторую команду, если предыдущая завершилась с кодом выхода 0. Это отражает оболочку &&.
#|| условно выполняет третью команду, если код выхода предыдущей команды отличается от нуля. Это отражает оболочку ||.Наконец, ! в конце выполняет команды и возвращает код выхода. Всё, что будет напечатано, будет отправлено в стандартный вывод процесса Scala. Если мы захотим его захватить, мы могли бы запустить это с помощью !! вместо этого.
Примечание: хотя это не показано выше, эквивалентом оболочной ; будет ###. Причина этого имени заключается в том, что ; — это зарезервированный символ в Scala.
трайт ProcessCreation extends AnyRef
Фабрики для создания scala.sys.process.ProcessBuilder. Их можно найти и использовать через компаньон-объект scala.sys.process.Process.
final класс ProcessIO extends AnyRef
Этот класс используется для управления Ввод/выводом каждого процесса scala.sys.process.Process. Функции, используемые для его создания, будут вызваны с потоками процесса после его запуска. Возможно, использование ProcessIO напрямую не требуется — scala.sys.process.ProcessBuilder может вернуть выходные данные процесса вызывающей стороне, или можно использовать scala.sys.process.ProcessLogger, что позволяет избежать прямого взаимодействия с потоком. Можно даже использовать фабрики в BasicIO для создания ProcessIO, или использовать его вспомогательные методы при создании собственного ProcessIO.
При создании ProcessIO, важно закрыть все потоки по завершении, так как JVM может использовать системные ресурсы для захвата входных и выходных данных процесса и не освободит их, пока потоки не будут явно закрыты.
ProcessBuilder вызовет writeInput, processOutput и processError в отдельных потоках, и если daemonizeThreads имеет значение true, все они будут помечены как демонические потоки.
- Примечание
Отказ от закрытия переданных потоков может привести к утечке ресурсов.
trait ProcessImplicits extends AnyRef
Предоставляет неявные преобразования для фабрик, предлагаемых компаньоном объекта scala.sys.process.Process. Эти неявные преобразования могут использоваться для уменьшения шума в цепочке команд, делая ее более похожей на командную оболочку. Они доступны через пакетный объект scala.sys.process.
trait ProcessLogger extends AnyRef
Инкапсулирует потоки вывода и ошибок работающего процесса. Используется scala.sys.process.ProcessBuilder при запуске процесса в качестве альтернативы scala.sys.process.ProcessIO, который может быть сложнее в использовании. Обратите внимание, что для создания ProcessIO все равно будет использоваться ProcessLogger. Объект BasicIO содержит некоторые функции для этого.
Вот пример, который подсчитывает количество строк в нормальном и ошибочном выводе процесса:
import scala.sys.process._
var normalLines = 0
var errorLines = 0
val countLogger = ProcessLogger(line => normalLines += 1,
line => errorLines += 1)
"find /etc" ! countLogger
- См. также
Члены-значения
неявный def builderToProcess(builder: JProcessBuilder): ProcessBuilder
Неявное преобразование java.lang.ProcessBuilder в Scala.
- Определения классов
- ProcessImplicits
неявный def buildersToProcess[T](builders: collection.Seq[T])(неявный convert: (T) => Source): collection.Seq[Source]
Возвращает последовательность scala.sys.process.ProcessBuilder.Source из последовательности значений, для которых доступно неявное преобразование в Source.
- Определения классов
- ProcessImplicits
неявный def fileToProcess(file: File): FileBuilder
Неявное преобразование java.io.File в scala.sys.process.ProcessBuilder.FileBuilder, которое может использоваться в качестве входных или выходных данных процесса. Например:
import scala.sys.process._
"ls" #> new java.io.File("dirContents.txt") !
- Определения классов
- ProcessImplicits
def stderr: PrintStream
def stdin: InputStream
def stdout: PrintStream
неявный def stringSeqToProcess(command: collection.Seq[String]): ProcessBuilder
Неявное преобразование последовательности String в scala.sys.process.ProcessBuilder. Первый аргумент будет принят как команда для выполнения, а остальные — как её аргументы. При использовании аргументы могут содержать пробелы.
- Определения классов
- ProcessImplicits
неявный def stringToProcess(command: String): ProcessBuilder
Неявное преобразование String в scala.sys.process.ProcessBuilder.
- Определения классов
- ProcessImplicits
неявный def urlToProcess(url: URL): URLBuilder
Неявное преобразование java.net.URL в scala.sys.process.ProcessBuilder.URLBuilder, которое может использоваться в качестве входных данных для процесса. Например:
import scala.sys.process._
Seq("xmllint", "--html", "-") #< new java.net.URL("http://www.scala-lang.org") #> new java.io.File("fixed.html") !
- Определения классов
- ProcessImplicits
object BasicIO
Этот объект содержит фабрики для scala.sys.process.ProcessIO, которые могут использоваться для управления вводом/выводом scala.sys.process.Process при запуске scala.sys.process.ProcessBuilder с командой run.
Он также содержит некоторые вспомогательные методы, которые могут использоваться при создании ProcessIO.
Используется другими классами пакета для реализации различных функций, но также может использоваться клиентским кодом.
object Process extends ProcessImpl with ProcessCreation
object ProcessBuilder extends ProcessBuilderImpl
object ProcessLogger
© 2002-2019 EPFL, with contributions from Lightbend.
Licensed under the Apache License, Version 2.0.
https://www.scala-lang.org/api/2.13.0/scala/sys/process/index.html
Этот пакет обрабатывает выполнение внешних процессов. Содержимое этого пакета можно разделить на три группы по их обязанностям:
Для простых случаев, единственная важная группа — первая. Запуск внешней команды может быть таким простым, как
"ls".!, или таким сложным, как построение цепочки команд, например:import scala.sys.process._ "ls" #| "grep .scala" #&& Seq("sh", "-c", "scalac *.scala") #|| "echo nothing found" lazyLinesНиже мы опишем общие понятия и архитектуру пакета, а затем подробнее рассмотрим каждую из упомянутых выше категорий.
Понятия и архитектура
Основой всего пакета являются классы Java
ProcessиProcessBuilder. Хотя нет необходимости использовать эти классы Java, они накладывают ограничения на возможности. Например, невозможно получить идентификатор процесса для выполняемого процесса.При выполнении внешнего процесса можно указать имя команды, аргументы к ней, каталог, в котором она будет выполнена, и переменные среды, которые будут установлены. Для каждого выполняемого процесса можно подавать стандартный ввод через
java.io.OutputStream, и считывать стандартный вывод и стандартную ошибку через паруjava.io.InputStream. Можно дождаться завершения процесса и получить его возвращаемое значение, или можно убить выполняемый процесс. Всё остальное должно быть построено на этих функциях.Этот пакет предоставляет DSL для запуска и объединения таких процессов, имитируя возможности оболочек Unix перенаправлять вывод одного процесса на ввод другого или контролировать выполнение последующих процессов на основе состояния возврата предыдущего.
В дополнение к этому DSL, этот пакет также предоставляет несколько способов управления вводом и выводом этих процессов, от простых и удобных до сложных и гибких.
Когда процессы объединяются, создаётся новый
ProcessBuilder, который при запуске выполнит экземплярыProcessBuilder, входящие в него, в соответствии с методом объединения. При перенаправлении одного процесса на другой они будут выполняться одновременно, и каждому будет переданProcessIO, который скопирует вывод одного на ввод другого.Что запускать и как
Центральным компонентом DSL выполнения процесса является трайт scala.sys.process.ProcessBuilder. Он
ProcessBuilderреализует DSL выполнения процесса, создаёт scala.sys.process.Process, который будет обрабатывать выполнение и возвращать результаты такого выполнения вызывающей стороне. Мы можем видеть этот DSL в примере из введения:#|,#&&и#!!— методыProcessBuilderиспользуемые для создания новогоProcessBuilderпосредством объединения.Создаётся
ProcessBuilderлибо с помощью фабрик в спутнике объекта scala.sys.process.Process, либо с помощью неявных преобразований, доступных в этом пакете объекта. Неявно, каждый процесс создаётся либо изString, где аргументы разделены пробелами — пробелы не могут быть экранированы — или из scala.collection.Seq, где первый элемент представляет имя команды, а остальные элементы — аргументы к ней. В последнем случае аргументы могут содержать пробелы.Для дальнейшего контроля способа выполнения процесса, например, для указания каталога, в котором он будет выполнен, см. фабрики в спутнике объекта scala.sys.process.Process.
После того, как желаемый
ProcessBuilderдоступен, он может быть выполнен различными способами, в зависимости от того, как вы хотите контролировать его ввод/вывод и какой результат вы хотите получить:!)String(методы!!)LazyList[String](методыlazyLines)Process(методыrun)Некоторые простые примеры этих методов:
import scala.sys.process._ // This uses ! to get the exit code def fileExists(name: String) = Seq("test", "-f", name).! == 0 // This uses !! to get the whole result as a string val dirContents = "ls".!! // This "fire-and-forgets" the method, which can be lazily read through // a LazyList[String] def sourceFilesAt(baseDir: String): LazyList[String] = { val cmd = Seq("find", baseDir, "-name", "*.scala", "-type", "f") cmd.lazyLines }Мы рассмотрим более подробное управление вводом/выводом процесса в следующем разделе.
Обработка ввода и вывода
В базовой модели Java, после запуска
Process, можно получитьjava.io.InputStreamиjava.io.OutputStream, представляющие соответственно его вывод и ввод. То есть то, что вы пишете вOutputStream, превращается во ввод для процесса, а вывод процесса можно прочитать изInputStream— из которых два, один представляет нормальный вывод, а другой — вывод ошибок.Эта модель создаёт трудность, заключающуюся в том, что код, ответственный за фактический запуск внешних процессов, должен принимать решения о том, как обрабатывать его ввод/вывод.
Этот пакет предлагает альтернативную модель: ввод/вывод работающего процесса контролируется объектом scala.sys.process.ProcessIO, который может передаваться _в_ код, запускающий внешний процесс. У
ProcessIOбудет прямой доступ к потокам Java, связанным с вводом/выводом процесса. Однако он должен затем закрыть эти потоки.Тем не менее, доступны более простые абстракции. Компоненты этого пакета, которые обрабатывают ввод/вывод:
ProcessIO.Примеры обработки ввода/вывода:
import scala.sys.process._ // An overly complex way of computing size of a compressed file def gzFileSize(name: String) = { val cat = Seq("zcat", name) var count = 0 def byteCounter(input: java.io.InputStream) = { while(input.read() != -1) count += 1 input.close() } val p = cat run new ProcessIO(_.close(), byteCounter, _.close()) p.exitValue() count } // This "fire-and-forgets" the method, which can be lazily read through // a LazyList[String], and accumulates all errors on a StringBuffer def sourceFilesAt(baseDir: String): (LazyList[String], StringBuffer) = { val buffer = new StringBuffer() val cmd = Seq("find", baseDir, "-name", "*.scala", "-type", "f") val lazyLines = cmd lazyLines_! ProcessLogger(buffer append _) (lazyLines, buffer) }Экземпляры классов Java
java.io.Fileиjava.net.URLмогут быть напрямую использованы как ввод для других процессов, аjava.io.File— и как вывод. Можно даже напрямую перенаправить один в другой без каких-либо промежуточных процессов, хотя это не является целью проектирования или рекомендуемой практикой. Например, следующий код скопирует веб-страницу в файл:import java.io.File import java.net.URL import scala.sys.process._ new URL("http://www.scala-lang.org/") #> new File("scala-lang.html") !Дополнительную информацию об остальных способах управления вводом/выводом можно найти в Scaladoc соответствующих объектов, трайтов и классов.
Выполнение процесса
Парадоксально, этот компонент самый простой и наименее вероятен для взаимодействия. Он состоит только из scala.sys.process.Process и предоставляет только два метода:
exitValue(): ожидает завершения процесса и возвращает значение выхода. Именно это происходит при использовании метода!объектаProcessBuilder.destroy(): это убьёт внешний процесс и закроет связанные с ним потоки.