URIS http://rubyforge.org/frs/?group_id=1024 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.5.1: - fixes a __critical__ but in ThreadEnsemble class that had a race condition that could cause thread deadlock. sorry bout that folks. 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 -a -- to foster inner awareness, introspection, and reasoning is more efficient than meditation and prayer. - h.h. the 14th dali lama