summaryrefslogtreecommitdiff
path: root/src/library/scala/sys/process/ProcessBuilder.scala
blob: 214d908012891f149ffb6db690084fb665b4a7f1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/*                     __                                               *\
**     ________ ___   / /  ___     Scala API                            **
**    / __/ __// _ | / /  / _ |    (c) 2003-2011, LAMP/EPFL             **
**  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
** /____/\___/_/ |_/____/_/ | |                                         **
**                          |/                                          **
\*                                                                      */

package scala.sys
package process

import processInternal._
import ProcessBuilder._

/** Represents a runnable process.
  *
  * This is the main component of this package. A `ProcessBuilder` may be composed with
  * others, either concatenating their outputs or piping them from one to the next, and
  * possibly with conditional execution depending on the last process exit value.
  *
  * Once executed, one can retrieve the output or redirect it to a
  * [[scala.sys.process.ProcessLogger]], or one can get the exit value, discarding or
  * redirecting the output.
  *
  * One creates a `ProcessBuilder` through factories provided in [[scala.sys.process.Process]]'s
  * companion object, or implicit conversions based on these factories made available in the
  * package object [[scala.sys.process]].
  *
  * Let's examine in detail one example of usage:
  *
  * {{{
  * import scala.sys.process._
  * "find src -name *.scala -exec grep null {} ;"  #|  "xargs test -z"  #&&  "echo null-free"  #||  "echo null detected"  !
  * }}}
  *
  * Note that every `String` is implicitly converted into a `ProcessBuilder`
  * through the implicits imported from [[scala.sys.process]]. These `ProcessBuilder` are then
  * combined in three different ways.
  *
  *   1. `#|` pipes the output of the first command into the input of the second command. It
  * mirrors a shell pipe (`|`).
  *   2. `#&&` conditionally executes the second command if the previous one finished with
  * exit value 0. It mirrors shell's `&&`.
  *   3. `#||` conditionally executes the third command if the exit value of the previous
  * command is different than zero. It mirrors shell's `&&`.
  *
  * Not shown here, the equivalent of a shell's `;` would be `###`. The reason for this name is
  * that `;` is a reserved token in Scala.
  *
  * Finally, `!` at the end executes the commands, and returns the exit value. If the output
  * was desired instead, one could run that with `!!` instead.
  *
  * If one wishes to execute the commands in background, one can either call `run`, which
  * returns a [[scala.sys.process.Process]] from which the exit value can be obtained, or
  * `lines`, which returns a [scala.collection.immutable.Stream] of output lines. This throws
  * an exception at the end of the `Stream` is the exit value is non-zero. To avoid exceptions,
  * one can use `lines_!` instead.
  *
  * One can also start the commands in specific ways to further control their I/O. Using `!<` to
  * start the commands will use the stdin from the current process for them. All methods can
  * be used passing a [[scala.sys.process.ProcessLogger]] to capture the output, both stderr and
  * stdout. And, when using `run`, one can pass a [[scala.sys.process.ProcessIO]] to control
  * stdin, stdout and stderr.
  *
  * The stdin of a command can be redirected from a `java.io.InputStream`, a `java.io.File`, a
  * `java.net.URL` or another `ProcessBuilder` through the method `#<`. Likewise, the stdout
  * can be sent to a `java.io.OutputStream`, a `java.io.File` or another `ProcessBuilder` with
  * the method `#>`. The method `#>>` can be used to append the output to a `java.io.File`.
  * For example:
  *
  * {{{
  * new URL("http://databinder.net/dispatch/About") #> "grep JSON" #>> new File("About_JSON") !
  * }}}
  */
trait ProcessBuilder extends Source with Sink {
  /** Starts the process represented by this builder, blocks until it exits, and returns the output as a String.  Standard error is
  * sent to the console.  If the exit code is non-zero, an exception is thrown.*/
  def !! : String
  /** Starts the process represented by this builder, blocks until it exits, and returns the output as a String.  Standard error is
  * sent to the provided ProcessLogger.  If the exit code is non-zero, an exception is thrown.*/
  def !!(log: ProcessLogger): String
  /** Starts the process represented by this builder.  The output is returned as a Stream that blocks when lines are not available
  * but the process has not completed.  Standard error is sent to the console.  If the process exits with a non-zero value,
  * the Stream will provide all lines up to termination and then throw an exception. */
  def lines: Stream[String]
  /** Starts the process represented by this builder.  The output is returned as a Stream that blocks when lines are not available
  * but the process has not completed.  Standard error is sent to the provided ProcessLogger.  If the process exits with a non-zero value,
  * the Stream will provide all lines up to termination but will not throw an exception. */
  def lines(log: ProcessLogger): Stream[String]
  /** Starts the process represented by this builder.  The output is returned as a Stream that blocks when lines are not available
  * but the process has not completed.  Standard error is sent to the console. If the process exits with a non-zero value,
  * the Stream will provide all lines up to termination but will not throw an exception. */
  def lines_! : Stream[String]
  /** Starts the process represented by this builder.  The output is returned as a Stream that blocks when lines are not available
  * but the process has not completed.  Standard error is sent to the provided ProcessLogger. If the process exits with a non-zero value,
  * the Stream will provide all lines up to termination but will not throw an exception. */
  def lines_!(log: ProcessLogger): Stream[String]
  /** Starts the process represented by this builder, blocks until it exits, and returns the exit code.  Standard output and error are
  * sent to the console.*/
  def ! : Int
  /** Starts the process represented by this builder, blocks until it exits, and returns the exit code.  Standard output and error are
  * sent to the given ProcessLogger.*/
  def !(log: ProcessLogger): Int
  /** Starts the process represented by this builder, blocks until it exits, and returns the exit code.  Standard output and error are
  * sent to the console.  The newly started process reads from standard input of the current process.*/
  def !< : Int
  /** Starts the process represented by this builder, blocks until it exits, and returns the exit code.  Standard output and error are
  * sent to the given ProcessLogger.  The newly started process reads from standard input of the current process.*/
  def !<(log: ProcessLogger): Int
  /** Starts the process represented by this builder.  Standard output and error are sent to the console.*/
  def run(): Process
  /** Starts the process represented by this builder.  Standard output and error are sent to the given ProcessLogger.*/
  def run(log: ProcessLogger): Process
  /** Starts the process represented by this builder.  I/O is handled by the given ProcessIO instance.*/
  def run(io: ProcessIO): Process
  /** Starts the process represented by this builder.  Standard output and error are sent to the console.
  * The newly started process reads from standard input of the current process if `connectInput` is true.*/
  def run(connectInput: Boolean): Process
  /** Starts the process represented by this builder, blocks until it exits, and returns the exit code.  Standard output and error are
  * sent to the given ProcessLogger.
  * The newly started process reads from standard input of the current process if `connectInput` is true.*/
  def run(log: ProcessLogger, connectInput: Boolean): Process

  /** Constructs a command that runs this command first and then `other` if this command succeeds.*/
  def #&& (other: ProcessBuilder): ProcessBuilder
  /** Constructs a command that runs this command first and then `other` if this command does not succeed.*/
  def #|| (other: ProcessBuilder): ProcessBuilder
  /** Constructs a command that will run this command and pipes the output to `other`.  `other` must be a simple command.*/
  def #| (other: ProcessBuilder): ProcessBuilder
  /** Constructs a command that will run this command and then `other`.  The exit code will be the exit code of `other`.*/
  def ### (other: ProcessBuilder): ProcessBuilder

  /** True if this command can be the target of a pipe.
   */
  def canPipeTo: Boolean

  /** True if this command has an exit code which should be propagated to the user.
   *  Given a pipe between A and B, if B.hasExitValue is true then the exit code will
   *  be the one from B; if it is false, the one from A.  This exists to prevent output
   *  redirections (implemented as pipes) from masking useful process error codes.
   */
  def hasExitValue: Boolean
}

/** This object contains traits used to describe input and output sources. */
object ProcessBuilder extends ProcessBuilderImpl {
  /** Used when creating [[scala.sys.process.ProcessBuilder.Source]] from an URL. */
  trait URLBuilder extends Source {

  }

  /** Used when creating [[scala.sys.process.ProcessBuilder.Source]] and/or
    * [[scala.sys.process.ProcessBuilder.Sink]] from a file.
    */
  trait FileBuilder extends Sink with Source {
    /** Append the contents of a `java.io.File` to this file */
    def #<<(f: File): ProcessBuilder

    /** Append the contents from a `java.net.URL` to this file */
    def #<<(u: URL): ProcessBuilder

    /** Append the contents of a `java.io.InputStream` to this file */
    def #<<(i: => InputStream): ProcessBuilder

    /** Append the contents of a [[scala.sys.process.ProcessBuilder]] to this file */
    def #<<(p: ProcessBuilder): ProcessBuilder
  }

  /** Represents everything that can be used as an input to a
    * [[scala.sys.process.ProcessBuilder]].
    */
  trait Source {
    protected def toSource: ProcessBuilder

    /** Writes the output stream of this process to the given file. */
    def #> (f: File): ProcessBuilder = toFile(f, false)

    /** Appends the output stream of this process to the given file. */
    def #>> (f: File): ProcessBuilder = toFile(f, true)

    /** Writes the output stream of this process to the given OutputStream. The
      * argument is call-by-name, so the stream is recreated, written, and closed each
      * time this process is executed.
      */
    def #>(out: => OutputStream): ProcessBuilder = #> (new OStreamBuilder(out, "<output stream>"))

    /** Writes the output stream of this process to a [[scala.sys.process.ProcessBuilder]]. */
    def #>(b: ProcessBuilder): ProcessBuilder = new PipedBuilder(toSource, b, false)

    /** Returnes a [[scala.sys.process.ProcessBuilder]] representing this `Source`. */
    def cat = toSource
    private def toFile(f: File, append: Boolean) = #> (new FileOutput(f, append))
  }

  /** Represents everything that can receive an output from a
    * [[scala.sys.process.ProcessBuilder]].
    */
  trait Sink {
    protected def toSink: ProcessBuilder

    /** Reads the given file into the input stream of this process. */
    def #< (f: File): ProcessBuilder = #< (new FileInput(f))

    /** Reads the given URL into the input stream of this process. */
    def #< (f: URL): ProcessBuilder = #< (new URLInput(f))

    /** Reads the given InputStream into the input stream of this process. The
      * argument is call-by-name, so the stream is recreated, read, and closed each
      * time this process is executed.
      */
    def #<(in: => InputStream): ProcessBuilder = #< (new IStreamBuilder(in, "<input stream>"))

    /** Reads the output of a [[scala.sys.process.ProcessBuilder]] into the input stream of this process. */
    def #<(b: ProcessBuilder): ProcessBuilder = new PipedBuilder(b, toSink, false)
  }
}