ahoward / open4

open child process with handles on pid, stdin, stdout, and stderr: manage child processes and their io handles easily.
Other
194 stars 27 forks source link

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.

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)

pfork4 is similar to popen4, but instead of executing a command, it runs ruby code in a child process. if the child process raises an exception, it propagates to the parent.

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

echo = lambda do
  $stdout.write $stdin.read
  raise 'finish implementing me'
end

org_message = "hello, world!"
got_message = nil
exception   = nil

begin
  Open4.pfork4(echo) do |cid, stdin, stdout, stderr|
    stdin.write org_message
    stdin.close
    got_message = stdout.read
  end
rescue RuntimeError => e
  exception = e.to_s
end

puts "org_message: #{org_message}"
puts "got_message: #{got_message}"
puts "exception  : #{exception}"

harp: > ruby sample/pfork4.rb
org_message: hello, world!
got_message: hello, world!
exception  : finish implementing me

HISTORY 1.0.0

AUTHOR

ara.t.howard@gmail.com

LICENSE

ruby's