module Subprocess

A Ruby clone of Python's subprocess module.

@see docs.python.org/2/library/subprocess.html

Constants

PIPE

An opaque constant that indicates that a pipe should be opened.

STDOUT

An opaque constant that can be passed to the `:stderr` option that indicates that the standard error stream should be redirected to the standard output.

VERSION

Public Class Methods

call(cmd, opts={}, &blk) click to toggle source

Call and wait for the return of a given process.

@note If you call this function with `:stdout => PIPE` or `:stderr => PIPE`,

this function will block indefinitely as soon as the OS's pipe buffer
fills up, as neither file descriptor will be read from. To avoid this, use
{Process#communicate} from a passed block.

@param [Array<String>] cmd See {Process#initialize} @param [Hash] opts See {Process#initialize} @yield [process] See {Process#initialize} @yieldparam process [Process] See {Process#initialize}

@return [::Process::Status] The exit status of the process

@see Process#initialize

# File lib/subprocess.rb, line 45
def self.call(cmd, opts={}, &blk)
  Process.new(cmd, opts, &blk).wait
end
check_call(cmd, opts={}, &blk) click to toggle source

Like {Subprocess::call}, except raise a {NonZeroExit} if the process did not terminate successfully.

@example Grep a file for a string

Subprocess.check_call(%W{grep -q llama ~/favorite_animals})

@example Communicate with a child process

Subprocess.check_call(%W{sendmail -t}, :stdin => Subprocess::PIPE) do |p|
  p.communicate <<-EMAIL
From: alpaca@example.com
To: llama@example.com
Subject: I am so fluffy.

SO FLUFFY!
http://upload.wikimedia.org/wikipedia/commons/3/3e/Unshorn_alpaca_grazing.jpg
  EMAIL
end

@note If you call this function with `:stdout => PIPE` or `:stderr => PIPE`,

this function will block indefinitely as soon as the OS's pipe buffer
fills up, as neither file descriptor will be read from. To avoid this, use
{Process#communicate} from a passed block.

@param [Array<String>] cmd See {Process#initialize} @param [Hash] opts See {Process#initialize} @yield [process] See {Process#initialize} @yieldparam process [Process] See {Process#initialize}

@raise [NonZeroExit] if the process returned a non-zero exit status (i.e.,

was terminated with an error or was killed by a signal)

@return [::Process::Status] The exit status of the process

@see Process#initialize

# File lib/subprocess.rb, line 82
def self.check_call(cmd, opts={}, &blk)
  status = Process.new(cmd, opts, &blk).wait
  raise NonZeroExit.new(cmd, status) unless status.success?
  status
end
check_output(cmd, opts={}, &blk) click to toggle source

Like {Subprocess::check_call}, but return the contents of `stdout`, much like {::Kernel#system}.

@example Get the system load

system_load = Subprocess.check_output(['uptime']).split(' ').last(3)

@param [Array<String>] cmd See {Process#initialize} @param [Hash] opts See {Process#initialize} @yield [process] See {Process#initialize} @yieldparam process [Process] See {Process#initialize}

@raise [NonZeroExit] if the process returned a non-zero exit status (i.e.,

was terminated with an error or was killed by a signal)

@return [String] The contents of `stdout`

@see Process#initialize

# File lib/subprocess.rb, line 104
def self.check_output(cmd, opts={}, &blk)
  opts[:stdout] = PIPE
  child = Process.new(cmd, opts, &blk)
  output, _ = child.communicate()
  raise NonZeroExit.new(cmd, child.status) unless child.wait.success?
  output
end
popen(cmd, opts={}, &blk) click to toggle source

An alias for `Process.new`. Mostly here to better emulate the Python API.

@param [Array<String>] cmd See {Process#initialize} @param [Hash] opts See {Process#initialize} @yield [process] See {Process#initialize} @yieldparam process [Process] See {Process#initialize} @return [Process] A process with the given arguments

@see Process#initialize

# File lib/subprocess.rb, line 26
def self.popen(cmd, opts={}, &blk)
  Process.new(cmd, opts, &blk)
end
status_to_s(status, convert_high_exit=true) click to toggle source

Print a human readable interpretation of a process exit status.

@param [::Process::Status] status The status returned by `waitpid2`. @param [Boolean] convert_high_exit Whether to convert exit statuses greater

than 128 into the usual convention for exiting after trapping a signal.
(e.g. many programs will exit with status 130 after receiving a SIGINT /
signal 2.)

@return [String] Text interpretation

# File lib/subprocess.rb, line 121
def self.status_to_s(status, convert_high_exit=true)
  # use an array just in case we somehow get a status with all the bits set
  parts = []
  if status.exited?
    parts << "exited with status #{status.exitstatus}"
    if convert_high_exit && status.exitstatus > 128
      # convert high exit statuses into what the original signal may have
      # been according to the usual exit status convention
      sig_num = status.exitstatus - 128

      sig_name = Signal.signame(sig_num)

      if sig_name
        parts << "(maybe SIG#{sig_name})"
      end
    end
  end
  if status.signaled?
    parts << "killed by signal #{status.termsig}"
  end
  if status.stopped?
    parts << "stopped by signal #{status.stopsig}"
  end

  if parts.empty?
    raise ArgumentError.new("Don't know how to interpret #{status.inspect}")
  end

  parts.join(', ')
end