Class: POSIX::Spawn::Child

### Home Classes Methods

### In Files

- lib/posix/spawn/child.rb

### Parent

Object

### Methods

- ::build
- ::new
- #exec!
- #success?

### Included Modules

- POSIX::Spawn

### Files

- COPYING
- HACKING

### Class/Module Index

Quicksearch

- POSIX
- POSIX::Spawn
- POSIX::Spawn::Child
- POSIX::Spawn::MaximumOutputExceeded
- POSIX::Spawn::TimeoutExceeded
- IO

No matching classes.

# POSIX::Spawn::Child

POSIX::Spawn::Child includes logic for executing child processes and reading/writing from their standard input, output, and error streams. It's designed to take all input in a single string and provides all output (stderr and stdout) as single strings and is therefore not well-suited to streaming large quantities of data in and out of commands.

Create and run a process to completion:

    >> child = POSIX::Spawn::Child.new('git', '--help')

Retrieve stdout or stderr output:

    >> child.out
    => "usage: git [--version] [--exec-path[=GIT_EXEC_PATH]]\n ..."
    >> child.err
    => ""

Check process exit status information:

    >> child.status
    => #<Process::Status: pid=80718,exited(0)>

To write data on the new process's stdin immediately after spawning:

    >> child = POSIX::Spawn::Child.new('bc', :input => '40 + 2')
    >> child.out
    "42\n"

To access output from the process even if an exception was raised:

    >> child = POSIX::Spawn::Child.build('git', 'log', :max => 1000)
    >> begin
    ?>   child.exec!
    ?> rescue POSIX::Spawn::MaximumOutputExceeded
    ?>   # just so you know
    ?> end
    >> child.out
    "... first 1000 characters of log output ..."

Q: Why use POSIX::Spawn::Child instead of popen3, hand rolled fork/exec code, or Process::spawn?

- It's more efficient than popen3 and provides meaningful process hierarchies because it performs a single fork/exec. (popen3 double forks to avoid needing to collect the exit status and also calls Process::detach which creates a Ruby Thread!!!!).

- It handles all max pipe buffer (PIPE_BUF) hang cases when reading and writing semi-large amounts of data. This is non-trivial to implement correctly and must be accounted for with popen3, spawn, or hand rolled fork/exec code.

- It's more portable than hand rolled pipe, fork, exec code because fork(2) and exec aren't available on all platforms. In those cases, POSIX::Spawn::Child falls back to using whatever janky substitutes the platform provides.

### Constants

BUFSIZE

  Maximum buffer size for reading

### Attributes

err[R]

All data written to the child process's stderr stream as a String.

out[R]

All data written to the child process's stdout stream as a String.

runtime[R]

Total command execution time (wall-clock time)

status[R]

A Process::Status object with information on how the child exited.

### Public Class Methods

build(*args) click to toggle source

Set up a new process to spawn, but do not actually spawn it.

Invoke this just like the normal constructor to set up a process to be run. Call `exec!` to actually run the child process, send the input, read the output, and wait for completion. Use this alternative way of constructing a POSIX::Spawn::Child if you want to read any partial output from the child process even after an exception.

    child = POSIX::Spawn::Child.build(... arguments ...)
    child.exec!

The arguments are the same as the regular constructor.

Returns a new Child instance but does not run the underlying process.

    # File lib/posix/spawn/child.rb, line 109
    def self.build(*args)
      options =
        if args[-1].respond_to?(:to_hash)
          args.pop.to_hash
        else
          {}
        end
      new(*(args + [{ :noexec => true }.merge(options)]))
    end

new(*args) click to toggle source

Spawn a new process, write all input and read all output, and wait for the program to exit. Supports the standard spawn interface as described in the POSIX::Spawn module documentation:

    new([env], command, [argv1, ...], [options])

The following options are supported in addition to the standard POSIX::Spawn options:

    :input   => str      Write str to the new process's standard input.
    :timeout => int      Maximum number of seconds to allow the process
                         to execute before aborting with a TimeoutExceeded
                         exception.
    :max     => total    Maximum number of bytes of output to allow the
                         process to generate before aborting with a
                         MaximumOutputExceeded exception.

Returns a new Child instance whose underlying process has already executed to completion. The out, err, and status attributes are immediately available.

    # File lib/posix/spawn/child.rb, line 84
    def initialize(*args)
      @env, @argv, options = extract_process_spawn_arguments(*args)
      @options = options.dup
      @input = @options.delete(:input)
      @timeout = @options.delete(:timeout)
      @max = @options.delete(:max)
      @options.delete(:chdir) if @options[:chdir].nil?
      exec! if !@options.delete(:noexec)
    end

### Public Instance Methods

exec!() click to toggle source

Execute command, write input, and read output. This is called immediately when a new instance of this object is created, or can be called explicitly when creating the Child via `build`.

    # File lib/posix/spawn/child.rb, line 139
    def exec!
      # spawn the process and hook up the pipes
      pid, stdin, stdout, stderr = popen4(@env, *(@argv + [@options]))

      # async read from all streams into buffers
      read_and_write(@input, stdin, stdout, stderr, @timeout, @max)

      # grab exit status
      @status = waitpid(pid)
    rescue Object => boom
      [stdin, stdout, stderr].each { |fd| fd.close rescue nil }
      if @status.nil?
        ::Process.kill('TERM', pid) rescue nil
        @status = waitpid(pid)      rescue nil
      end
      raise
    ensure
      # let's be absolutely certain these are closed
      [stdin, stdout, stderr].each { |fd| fd.close rescue nil }
    end

success?() click to toggle source

Determine if the process did exit with a zero exit status.

    # File lib/posix/spawn/child.rb, line 132
    def success?
      @status && @status.success?
    end

[Validate]

Generated with the Darkfish Rdoc Generator 2.