Module: Subprocess

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

Overview

A Ruby clone of Python's subprocess module.

Defined Under Namespace

Classes: CommunicateTimeout, NonZeroExit, Process

Constant Summary collapse

PIPE =

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

-1
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.5.4'

Class Method Summary collapse

Class Method Details

.call(cmd, opts = {}) {|process| ... } ⇒ ::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.

Parameters:

Yields:

Yield Parameters:

Returns:

  • (::Process::Status)

    The exit status of the process

See Also:


45
46
47
# File 'lib/subprocess.rb', line 45

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

.check_call(cmd, opts = {}) {|process| ... } ⇒ ::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

Parameters:

Yields:

Yield Parameters:

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:


82
83
84
85
86
# 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 = {}) {|process| ... } ⇒ 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)

Parameters:

Yields:

Yield Parameters:

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:


104
105
106
107
108
109
110
# 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 = {}) {|process| ... } ⇒ Process

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

Parameters:

Yields:

Yield Parameters:

Returns:

  • (Process)

    A process with the given arguments

See Also:


26
27
28
# 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) ⇒ 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


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