summaryrefslogblamecommitdiff
path: root/src/library/scala/sys/process/ProcessBuilder.scala
blob: 214d908012891f149ffb6db690084fb665b4a7f1 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11

                                                                          
                                                                          







                                                                          
                        

                       





























                                                                                                                           
                                                            




























                                                                                                
                                               














































                                                                                                                                          
 







                                                                                                                           
 

                                                       
                        






                                                                                       

 
                                                                             
                                                  
                                                                                    


                                   



                                                                           
                                              
                                                               
                                    

                                                                 
                                   

                                                                      
                                              

                                                                                     
                                              
   



                                                             

                                          
 

                                                                      
 

                                                                       
 
                                                                               


                                                                                      
                                                                                                  

                                                                                              
                                                                                    

                                                                                      

                                                                                 
   



                                                             
              
                                        
 

                                                                      
 

                                                                     
 
                                                                              


                                                                                   
                                                                                              

                                                                                                            
                                                                                  

   
/*                     __                                               *\
**     ________ ___   / /  ___     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)
  }
}