Module: Subprocess

Defined in:
lib/subprocess.rb,
lib/subprocess/version.rb

Overview

A Ruby clone of Python’s subprocess module.

Defined Under Namespace

Classes: NonZeroExit, Process

Constant Summary collapse

PIPE =

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

-1
# 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.
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.

-2
VERSION =
'1.3.2'

Class Method Summary collapse

Class Method Details

.call(cmd, opts = {}, &blk) ⇒ ::Process::Status

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 Subprocess::Process#communicate from a passed block.

Call and wait for the return of a given process.

Returns:

  • (::Process::Status)

    The exit status of the process

See Also:

  • {Process{Process#initialize}


35
36
37
# File 'lib/subprocess.rb', line 35

def self.call(cmd, opts={}, &blk)
  Process.new(cmd, opts, &blk).wait
end

.check_call(cmd, opts = {}, &blk) ⇒ ::Process::Status

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 Subprocess::Process#communicate from a passed block.

Like call, except raise a NonZeroExit if the process did not terminate successfully.

Examples:

Grep a file for a string

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

Communicate with a child process

Subprocess.check_call(%W{sendmail -t}, :stdin => Subprocess::PIPE) do |p|
  p.communicate <<-EMAIL
From: [email protected]
To: [email protected]
Subject: I am so fluffy.

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

Returns:

  • (::Process::Status)

    The exit status of the process

Raises:

  • (NonZeroExit)

    if the process returned a non-zero exit status (i.e., was terminated with an error or was killed by a signal)

See Also:

  • {Process{Process#initialize}


67
68
69
70
71
# File 'lib/subprocess.rb', line 67

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) ⇒ String

Like check_call, but return the contents of ‘stdout`, much like `Kernel#system`.

Examples:

Get the system load

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

Returns:

  • (String)

    The contents of ‘stdout`

Raises:

  • (NonZeroExit)

    if the process returned a non-zero exit status (i.e., was terminated with an error or was killed by a signal)

See Also:

  • {Process{Process#initialize}


84
85
86
87
88
89
90
# File 'lib/subprocess.rb', line 84

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) ⇒ Process

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

Returns:

  • (Process)

    A process with the given arguments



21
22
23
# File 'lib/subprocess.rb', line 21

def self.popen(cmd, opts={}, &blk)
  Process.new(cmd, opts, &blk)
end

.status_to_s(status, convert_high_exit = true) ⇒ String

Print a human readable interpretation of a process exit status.

Parameters:

  • status (::Process::Status)

    The status returned by ‘waitpid2`.

  • convert_high_exit (Boolean) (defaults to: true)

    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.)

Returns:

  • (String)

    Text interpretation



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
# File 'lib/subprocess.rb', line 101

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