URIS

http://rubyforge.org/projects/codeforpeople/
http://www.codeforpeople.com/lib/ruby/

SYNOPSIS

open child process with handles on pid, stdin, stdout, and stderr: manage
child processes and their io handles easily.

HISTORY

0.9.1:
  - fixed warning with '-w' : @cid not initialized.  thanks blaise tarr.

0.9.0:
  - added the ability for open4.spawn to take either an array of arguments
    or multiple arguments in order to specify the argv for the command run.
    for example

      open4.spawn ['touch', 'difficult to "quote"'], :stdout=>STDOUT

    same thing

      open4.spawn 'touch', 'difficult to "quote"', :stdout=>STDOUT

    thanks to jordan breeding for this suggestion

  - added 'cwd'/:cwd keyword.  usage is pretty obivous

      open4.spawn 'pwd', 1=>STDOUT, :cwd=>'/tmp'   #=> /tmp

    this one also from jordan

0.8.0:

  - fixed a critical bug whereby a process producing tons of stdout, but for
    which the stdout was not handled, would cause the child process to
    become blocked/hung writing to the pipe.  eg, this command would cause a
    hang

      include Open4

      spawn 'ruby -e"  puts Array.new(65536){ 42 }  "'

    whereas this one would not

      include Open4

      spawn 'ruby -e"  puts Array.new(65536){ 42 }  "', :stdout=>StringIO.new

    this version handles the former by spawning a 'null' thread which reads,
    but does not process stdout/stderr.  that way commands which generate
    tons of output will never become blocked.

0.7.0:
  - merged functionality of exitstatus/status keywords:

      include Open4

      spawn 'ruby -e "exit 42"'                 # raises 
      spawn 'ruby -e "exit 42"', :status=>true  # ok, returns status
      spawn 'ruby -e "exit 42"', :status=>42    # raises if status != 42
      spawn 'ruby -e "exit 42"', :status=>0,42  # raises if status != 0||42

  - the 0.6.0 was broken on rubyforge... this release fixes that (somehow!?)

0.6.0:
  - added feature for exitstatus to be list of acceptable exit statuses

      Open4.spawn 'ruby -e "exit 42"'                      # raises
      Open4.spawn 'ruby -e "exit 42"', :exitstatus=>[0,42] # ok

  - added :status switch, which will always simply return the status (no
    error thrown for failure)

      Open4.spawn 'ruby -e "exit 42"'                          # raises 
      status = Open4.spawn 'ruby -e "exit 42"', :status=>true  # ok 

    note, however, that any SpawnError does in fact contain the failed
    status so, even when they are thrown, error status can be retrieved:

      include Open4

      status =
        begin
          spawn 'ruby -e "exit 42"'
        rescue SpawnError => e
          warn{ e }
          e.status
        end

0.5.1:
  - fixes a __critical__ but in ThreadEnsemble class that had a race
    condition that could cause thread deadlock.  sorry bout that folks.

0.5.0:
  - on the suggestion of tim pease (thanks tim!), i added timeout features
    to open4.  the command run may have an overall timeout and individual
    timeouts set for each of the io handles.  for example

      cmd = 'command_that_produce_out_at_one_second_intervals'

      open4.spawn cmd, :stdout_timeout => 2 

    or 

      cmd = 'command_that_should_complete_in_about_one_minute'

      open4.spawn cmd, :timeout => 60

    or

      cmd = 'consumes_input_at_one_line_per_second_rate'

      input = %w( 42 forty-two 42.0 )

      open4.spawn cmd, :stdin=>input, :stdin_timeout=>1

  - added 'open4' alias so one can write

      open4.spawn  vs Open4.spawn

    or even

      open4(cmd) do |pid,i,o,e|
      end

  - added signal info to SpawnError

0.4.0:
  - improved error handling contributed by jordan breeding.
  - introduction of background/bg method

0.3.0 :
  - bug fix from jordan breeding.  general clean up.  added spawn method.

0.2.0 :
  - added exception marshaled from child -> parent when exec fails.  thanks
    to jordan breeding for a patch (yay!) and paul brannan for this most
    excellent idea.

0.1.0 :
  - fixed docs to correctly show return value of popen4 (pid first not last).
    thanks Stefanie Tellex <[email protected]> for catching this. 
0.0.0 :
  - initial version

INSTALL

~> gem install open4

SAMPLES

----------------------------------------------------------------------------
simple usage
----------------------------------------------------------------------------

  harp: > cat sample/simple.rb
  require "open4"

  pid, stdin, stdout, stderr = Open4::popen4 "sh"

  stdin.puts "echo 42.out"
  stdin.puts "echo 42.err 1>&2"
  stdin.close

  ignored, status = Process::waitpid2 pid

  puts "pid        : #{ pid }"
  puts "stdout     : #{ stdout.read.strip }"
  puts "stderr     : #{ stderr.read.strip }"
  puts "status     : #{ status.inspect }"
  puts "exitstatus : #{ status.exitstatus }"

  harp: > ruby sample/simple.rb
  pid        : 17273
  stdout     : 42.out
  stderr     : 42.err
  status     : #<Process::Status: pid=17273,exited(0)>
  exitstatus : 0

----------------------------------------------------------------------------
in block form - the child process is automatically waited for
----------------------------------------------------------------------------

  harp: > cat sample/block.rb
  require 'open4'

  status =
    Open4::popen4("sh") do |pid, stdin, stdout, stderr|
      stdin.puts "echo 42.out"
      stdin.puts "echo 42.err 1>&2"
      stdin.close

      puts "pid        : #{ pid }"
      puts "stdout     : #{ stdout.read.strip }"
      puts "stderr     : #{ stderr.read.strip }"
    end

      puts "status     : #{ status.inspect }"
      puts "exitstatus : #{ status.exitstatus }"

  harp: > ruby sample/block.rb
  pid        : 17295
  stdout     : 42.out
  stderr     : 42.err
  status     : #<Process::Status: pid=17295,exited(0)>
  exitstatus : 0

----------------------------------------------------------------------------
exceptions are marshaled from child to parent if fork/exec fails
----------------------------------------------------------------------------

  harp: > cat sample/exception.rb
  require "open4"
  Open4::popen4 "noexist"

  harp: > ruby sample/exception.rb
  /dmsp/reference/ruby-1.8.1//lib/ruby/site_ruby/open4.rb:100:in `popen4': No such file or directory - noexist (Errno::ENOENT)
          from sample/exception.rb:3

----------------------------------------------------------------------------
the spawn method provides and even more convenient method of running a
process, allowing any object that supports 'each', 'read', or 'to_s' to be
given as stdin and any objects that support '<<' to be given as
stdout/stderr.  an exception is thrown if the exec'd cmd fails (nonzero
exitstatus) unless the option 'raise'=>false is given
----------------------------------------------------------------------------

  harp: > cat sample/spawn.rb
  require 'open4'
  include Open4

  cat = '  ruby -e"  ARGF.each{|line| STDOUT << line}  "  '

  stdout, stderr = '', ''
  status = spawn cat, 'stdin' => '42', 'stdout' => stdout, 'stderr' => stderr
  p status
  p stdout
  p stderr

  stdout, stderr = '', ''
  status = spawn cat, 0=>'42', 1=>stdout, 2=>stderr
  p status
  p stdout
  p stderr

  harp: > RUBYLIB=lib ruby sample/spawn.rb
  0
  "42"
  ""
  0
  "42"
  ""

----------------------------------------------------------------------------
the bg/background method is similar to spawn, but the process is
automatically set running in a thread.  the returned thread has several
methods added dynamically which return the pid and blocking calls to the
exitstatus.
----------------------------------------------------------------------------

  harp: > cat sample/bg.rb
  require 'yaml'
  require 'open4'
  include Open4

  stdin = '42'
  stdout = ''
  stderr = ''

  t = bg 'ruby -e"sleep 4; puts ARGF.read"', 0=>stdin, 1=>stdout, 2=>stderr

  waiter = Thread.new{ y t.pid => t.exitstatus } # t.exitstatus is a blocking call!

  while((status = t.status))
    y "status" => status
    sleep 1
  end

  waiter.join

  y "stdout" => stdout

  harp: > ruby sample/bg.rb
  ---
  status: run
  ---
  status: sleep
  ---
  status: sleep
  ---
  status: sleep
  ---
  21357: 0
  ---
  stdout: "42\n"

----------------------------------------------------------------------------
the timeout methods can be used to ensure execution is preceding at the
desired interval.  note also how to setup a 'pipeline'
----------------------------------------------------------------------------

  harp: > cat sample/stdin_timeout.rb
  require 'open4'

  producer = 'ruby -e" STDOUT.sync = true; loop{sleep(rand+rand) and puts 42} "'

  consumer = 'ruby -e" STDOUT.sync = true; STDIN.each{|line| puts line} "'

  open4(producer) do |pid, i, o, e|

    open4.spawn consumer, :stdin=>o, :stdout=>STDOUT, :stdin_timeout => 1.4

  end

  harp: > ruby sample/stdin_timeout.rb
  42
  42
  42
  42
  42
  /dmsp/reference/ruby-1.8.1//lib/ruby/1.8/timeout.rb:42:in `relay': execution expired (Timeout::Error)

AUTHOR

ara.t.howard@noaa.gov

LICENSE

ruby's