--Multipart_Wed_Feb_23_17:41:16_2005-1 Content-Type: text/plain; charset=US-ASCII Hello, >>>>> On Thu, 17 Feb 2005 04:21:41 +0900 >>>>> matz / ruby-lang.org(Yukihiro Matsumoto) said: matz> |Initial Comment: matz> |$ ruby -e 'require "open3"; stdin,stdout,stderr pen3.popen3("/bin/true"); stdin.close; puts $?' # prints 256 matz> |256 matz> open3 uses the "double fork" technique, so that you cannot take the matz> exit status of the command. But it doesn't have to be 256. Replacing exit! with exit!(true) is a simple solution. Following patch is a better exit status solution, but the child process remains after the grandchild exits. Index: lib/open3.rb RCS file: /src/ruby/lib/open3.rb,v retrieving revision 1.10 diff -u -p -r1.10 open3.rb --- lib/open3.rb 18 Sep 2003 13:43:42 -0000 1.10 +++ lib/open3.rb 23 Feb 2005 08:38:15 -0000 @@ -17,40 +17,54 @@ module Open3 pr O::pipe pe O::pipe - pid ork{ + child_pid ork{ # child - fork{ + pw[1].close + pr[0].close + pe[0].close + grandchild_pid ork{ # grandchild - pw[1].close STDIN.reopen(pw[0]) pw[0].close - pr[0].close STDOUT.reopen(pr[1]) pr[1].close - pe[0].close STDERR.reopen(pe[1]) pe[1].close exec(*cmd) + exit!(false) } - exit! + pw[0].close + pr[1].close + pe[1].close + STDIN.close + STDOUT.close + STDERR.close + grandchild_pid, status rocess.waitpid2(grandchild_pid) + if status.exited? + exit!(status.exitstatus) + else + # can not pass signaled, etc. + exit!(false) + end } pw[0].close pr[1].close pe[1].close - Process.waitpid(pid) pi pw[1], pr[0], pe[0]] pw[1].sync rue - if defined? yield + if block_given? begin - return yield(*pi) + ret ield(*pi) ensure pi.each{|p| p.close unless p.closed?} + Process.waitpid(child_pid) end end + Thread.start { Process.waitpid(child_pid) } pi end module_function :popen3 --Multipart_Wed_Feb_23_17:41:16_2005-1 Content-Type: text/x-ruby; charset=US-ASCII Content-Disposition: attachment; filename="test_open3.rb" Content-Transfer-Encoding: 7bit require 'test/unit' require 'open3' $:.replace([File.join(File.dirname(File.expand_path(__FILE__)), 'ruby')] | $:) require 'envutil' class TestOpen3 < Test::Unit::TestCase ruby nvUtil.rubybin system("ruby", "-e", "exit(true)") EXIT_SUCCESS ?.exitstatus system("ruby", "-e", "exit(false)") EXIT_FAILURE ?.exitstatus def test_popen3_without_block w, r, e pen3.popen3("cat") assert_nothing_raised do Thread.pass w.puts "test!" end assert_equal("test!\n", r.gets) assert_nothing_raised do Thread.pass w.puts "test!" w.close end assert_equal("test!\n", r.read) assert_nothing_raised { Thread.pass } # call waitpid # can not use $? since $? is in a thread local scope end def test_popen3_with_block Open3.popen3("cat") do |w, r, e| assert_nothing_raised do w.puts "test!" end assert_equal("test!\n", r.gets) assert_nothing_raised do w.puts "test!" w.close end assert_equal("test!\n", r.read) end assert_equal(true, $?.exited?) assert_equal(EXIT_SUCCESS, $?.exitstatus) end def test_popen3_with_false Open3.popen3("false") {|w, r, e| } assert_equal(true, $?.exited?) assert_equal(1, $?.exitstatus) end def test_popen3_with_true Open3.popen3("true") {|w, r, e| } assert_equal(true, $?.exited?) assert_equal(EXIT_SUCCESS, $?.exitstatus) end def test_popen3_with_exit_3 ruby nvUtil.rubybin Open3.popen3(ruby, "-e", "exit(3)") {|w, r, e| } assert_equal(true, $?.exited?) assert_equal(3, $?.exitstatus) end def test_system_with_signal ruby nvUtil.rubybin system(ruby, "-e", "Process.kill(:TERM, $$)") assert_equal(false, $?.exited?) assert_equal(true, $?.signaled?) assert_equal(Signal.list['TERM'], $?.termsig) end def test_popen3_with_signal ruby nvUtil.rubybin Open3.popen3(ruby, "-e", "Process.kill(:SEGV, $$)") {|w, r, e| } assert_equal(true, $?.exited?) assert_equal(false, $?.signaled?) assert_equal(EXIT_FAILURE, $?.exitstatus) end end --Multipart_Wed_Feb_23_17:41:16_2005-1--