mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* lib/open3.rb (Open3.popen3): merge hash options if given.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@20448 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
e44a6c4708
commit
852b4629c1
3 changed files with 298 additions and 19 deletions
|
@ -1,3 +1,7 @@
|
|||
Tue Dec 2 19:22:13 2008 Tanaka Akira <akr@fsij.org>
|
||||
|
||||
* lib/open3.rb (Open3.popen3): merge hash options if given.
|
||||
|
||||
Tue Dec 2 15:31:42 2008 Yukihiro Matsumoto <matz@ruby-lang.org>
|
||||
|
||||
* lib/net/protocol.rb (Net::BufferedIO#rbuf_fill): use
|
||||
|
|
108
lib/open3.rb
108
lib/open3.rb
|
@ -32,15 +32,23 @@
|
|||
#
|
||||
|
||||
module Open3
|
||||
#
|
||||
|
||||
# Open stdin, stdout, and stderr streams and start external executable.
|
||||
# In addition, a thread for waiting the started process is noticed.
|
||||
# The thread has a thread variable :pid which is the pid of the started
|
||||
# process.
|
||||
# The thread has a pid method and thread variable :pid which is the pid of
|
||||
# the started process.
|
||||
#
|
||||
# Block form:
|
||||
#
|
||||
# Open3.popen3(cmd... [, opts]) {|stdin, stdout, stderr, wait_thr|
|
||||
# pid = wait_thr.pid # pid of the started process.
|
||||
# ...
|
||||
# exit_status = wait_thr.value # Process::Status object returned.
|
||||
# }
|
||||
#
|
||||
# Non-block form:
|
||||
#
|
||||
# stdin, stdout, stderr, wait_thr = Open3.popen3(cmd)
|
||||
# stdin, stdout, stderr, wait_thr = Open3.popen3(cmd... [, opts])
|
||||
# pid = wait_thr[:pid] # pid of the started process.
|
||||
# ...
|
||||
# stdin.close # stdin, stdout and stderr should be closed in this form.
|
||||
|
@ -48,11 +56,43 @@ module Open3
|
|||
# stderr.close
|
||||
# exit_status = wait_thr.value # Process::Status object returned.
|
||||
#
|
||||
# Block form:
|
||||
# The parameters +cmd...+ is passed to Kernel#spawn.
|
||||
# So a commandline string and list of argument strings can be accepted as follows.
|
||||
#
|
||||
# Open3.popen3(cmd) { |stdin, stdout, stderr, wait_thr| ... }
|
||||
# Open3.popen3("echo a") {|i, o, e, t| ... }
|
||||
# Open3.popen3("echo", "a") {|i, o, e, t| ... }
|
||||
#
|
||||
# The parameter +cmd+ is passed directly to Kernel#spawn.
|
||||
# If the last parameter, opts, is a Hash, it is recognized as an option for Kernel#spawn.
|
||||
#
|
||||
# Open3.popen3("pwd", :chdir=>"/") {|i,o,e,t|
|
||||
# p o.read.chomp #=> "/"
|
||||
# }
|
||||
#
|
||||
# opts[STDIN], opts[STDOUT] and opts[STDERR] in the option are set for redirection.
|
||||
#
|
||||
# If some of the three elements in opts are specified,
|
||||
# pipes for them are not created.
|
||||
# In that case, block arugments for the block form and
|
||||
# return values for the non-block form are decreased.
|
||||
#
|
||||
# # No pipe "e" for stderr
|
||||
# Open3.popen3("echo a", STDERR=>nil) {|i,o,t| ... }
|
||||
# i,o,t = Open3.popen3("echo a", STDERR=>nil)
|
||||
#
|
||||
# If the value is nil as above, the elements of opts are removed.
|
||||
# So standard input/output/error of current process are inherited.
|
||||
#
|
||||
# If the value is not nil, it is passed as is to Kernel#spawn.
|
||||
# So pipeline of commands can be constracted as follows.
|
||||
#
|
||||
# Open3.popen3("yes", STDIN=>nil, STDERR=>nil) {|o1,t1|
|
||||
# Open3.popen3("head -10", STDIN=>o1, STDERR=>nil) {|o2,t2|
|
||||
# o1.close
|
||||
# p o2.read #=> "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"
|
||||
# p t1.value #=> #<Process::Status: pid 13508 SIGPIPE (signal 13)>
|
||||
# p t2.value #=> #<Process::Status: pid 13510 exit 0>
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# wait_thr.value waits the termination of the process.
|
||||
# The block form also waits the process when it returns.
|
||||
|
@ -60,26 +100,56 @@ module Open3
|
|||
# Closing stdin, stdout and stderr does not wait the process.
|
||||
#
|
||||
def popen3(*cmd)
|
||||
pw = IO::pipe # pipe[0] for read, pipe[1] for write
|
||||
pr = IO::pipe
|
||||
pe = IO::pipe
|
||||
if Hash === cmd.last
|
||||
opts = cmd.pop.dup
|
||||
else
|
||||
opts = {}
|
||||
end
|
||||
|
||||
pid = spawn(*cmd, STDIN=>pw[0], STDOUT=>pr[1], STDERR=>pe[1])
|
||||
child_io = []
|
||||
parent_io = []
|
||||
|
||||
if !opts.include?(STDIN)
|
||||
pw = IO.pipe # pipe[0] for read, pipe[1] for write
|
||||
opts[STDIN] = pw[0]
|
||||
pw[1].sync = true
|
||||
child_io << pw[0]
|
||||
parent_io << pw[1]
|
||||
elsif opts[STDIN] == nil
|
||||
opts.delete(STDIN)
|
||||
end
|
||||
|
||||
if !opts.include?(STDOUT)
|
||||
pr = IO.pipe
|
||||
opts[STDOUT] = pr[1]
|
||||
child_io << pr[1]
|
||||
parent_io << pr[0]
|
||||
elsif opts[STDOUT] == nil
|
||||
opts.delete(STDOUT)
|
||||
end
|
||||
|
||||
if !opts.include?(STDERR)
|
||||
pe = IO.pipe
|
||||
opts[STDERR] = pe[1]
|
||||
child_io << pe[1]
|
||||
parent_io << pe[0]
|
||||
elsif opts[STDERR] == nil
|
||||
opts.delete(STDERR)
|
||||
end
|
||||
|
||||
pid = spawn(*cmd, opts)
|
||||
wait_thr = Process.detach(pid)
|
||||
pw[0].close
|
||||
pr[1].close
|
||||
pe[1].close
|
||||
pi = [pw[1], pr[0], pe[0], wait_thr]
|
||||
pw[1].sync = true
|
||||
child_io.each {|io| io.close }
|
||||
result = [*parent_io, wait_thr]
|
||||
if defined? yield
|
||||
begin
|
||||
return yield(*pi)
|
||||
return yield(*result)
|
||||
ensure
|
||||
[pw[1], pr[0], pe[0]].each{|p| p.close unless p.closed?}
|
||||
parent_io.each{|io| io.close unless io.closed?}
|
||||
wait_thr.join
|
||||
end
|
||||
end
|
||||
pi
|
||||
result
|
||||
end
|
||||
module_function :popen3
|
||||
end
|
||||
|
|
205
test/test_open3.rb
Normal file
205
test/test_open3.rb
Normal file
|
@ -0,0 +1,205 @@
|
|||
require 'test/unit'
|
||||
require 'open3'
|
||||
require 'shellwords'
|
||||
require_relative 'ruby/envutil'
|
||||
|
||||
class TestOpen3 < Test::Unit::TestCase
|
||||
RUBY = EnvUtil.rubybin
|
||||
|
||||
def test_exit_status
|
||||
Open3.popen3(RUBY, '-e', 'exit true') {|i,o,e,t|
|
||||
assert_equal(true, t.value.success?)
|
||||
}
|
||||
Open3.popen3(RUBY, '-e', 'exit false') {|i,o,e,t|
|
||||
assert_equal(false, t.value.success?)
|
||||
}
|
||||
end
|
||||
|
||||
def test_stdin
|
||||
Open3.popen3(RUBY, '-e', 'exit STDIN.gets.chomp == "t"') {|i,o,e,t|
|
||||
i.puts 't'
|
||||
assert_equal(true, t.value.success?)
|
||||
}
|
||||
Open3.popen3(RUBY, '-e', 'exit STDIN.gets.chomp == "t"') {|i,o,e,t|
|
||||
i.puts 'f'
|
||||
assert_equal(false, t.value.success?)
|
||||
}
|
||||
end
|
||||
|
||||
def test_stdout
|
||||
Open3.popen3(RUBY, '-e', 'STDOUT.print "foo"') {|i,o,e,t|
|
||||
assert_equal("foo", o.read)
|
||||
}
|
||||
end
|
||||
|
||||
def test_stderr
|
||||
Open3.popen3(RUBY, '-e', 'STDERR.print "bar"') {|i,o,e,t|
|
||||
assert_equal("bar", e.read)
|
||||
}
|
||||
end
|
||||
|
||||
def test_block
|
||||
r = Open3.popen3(RUBY, '-e', 'STDOUT.print STDIN.read') {|i,o,e,t|
|
||||
i.print "baz"
|
||||
i.close
|
||||
assert_equal("baz", o.read)
|
||||
"qux"
|
||||
}
|
||||
assert_equal("qux", r)
|
||||
end
|
||||
|
||||
def test_noblock
|
||||
i,o,e,t = Open3.popen3(RUBY, '-e', 'STDOUT.print STDIN.read')
|
||||
i.print "baz"
|
||||
i.close
|
||||
assert_equal("baz", o.read)
|
||||
ensure
|
||||
i.close if !i.closed?
|
||||
o.close if !o.closed?
|
||||
e.close if !e.closed?
|
||||
end
|
||||
|
||||
def test_commandline
|
||||
commandline = Shellwords.join([RUBY, '-e', 'print "quux"'])
|
||||
Open3.popen3(commandline) {|i,o,e,t|
|
||||
assert_equal("quux", o.read)
|
||||
}
|
||||
end
|
||||
|
||||
def test_pid
|
||||
Open3.popen3(RUBY, '-e', 'print $$') {|i,o,e,t|
|
||||
pid = o.read.to_i
|
||||
assert_equal(pid, t[:pid])
|
||||
assert_equal(pid, t.pid)
|
||||
}
|
||||
end
|
||||
|
||||
def test_disable
|
||||
Open3.popen3(RUBY, '-e', '', STDIN=>nil) {|o,e,t|
|
||||
assert_kind_of(Thread, t)
|
||||
}
|
||||
Open3.popen3(RUBY, '-e', '', STDOUT=>nil) {|i,e,t|
|
||||
assert_kind_of(Thread, t)
|
||||
}
|
||||
Open3.popen3(RUBY, '-e', '', STDERR=>nil) {|i,o,t|
|
||||
assert_kind_of(Thread, t)
|
||||
}
|
||||
Open3.popen3(RUBY, '-e', '', STDIN=>nil, STDOUT=>nil, STDERR=>nil) {|t|
|
||||
assert_kind_of(Thread, t)
|
||||
}
|
||||
end
|
||||
|
||||
def with_pipe
|
||||
r, w = IO.pipe
|
||||
yield r, w
|
||||
ensure
|
||||
r.close if !r.closed?
|
||||
w.close if !w.closed?
|
||||
end
|
||||
|
||||
def with_reopen(io, arg)
|
||||
old = io.dup
|
||||
io.reopen(arg)
|
||||
yield old
|
||||
ensure
|
||||
io.reopen(old)
|
||||
old.close if old && !old.closed?
|
||||
end
|
||||
|
||||
def test_disable_stdin
|
||||
with_pipe {|r, w|
|
||||
with_reopen(STDIN, r) {|old|
|
||||
Open3.popen3(RUBY, '-e', 's=STDIN.read; STDOUT.print s+"o"; STDERR.print s+"e"', STDIN=>nil) {|o,e,t|
|
||||
assert_kind_of(Thread, t)
|
||||
w.print "x"
|
||||
w.close
|
||||
assert_equal("xo", o.read)
|
||||
assert_equal("xe", e.read)
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def test_disable_stdout
|
||||
with_pipe {|r, w|
|
||||
with_reopen(STDOUT, w) {|old|
|
||||
w.close
|
||||
Open3.popen3(RUBY, '-e', 's=STDIN.read; STDOUT.print s+"o"; STDERR.print s+"e"', STDOUT=>nil) {|i,e,t|
|
||||
assert_kind_of(Thread, t)
|
||||
i.print "y"
|
||||
i.close
|
||||
STDOUT.reopen(old)
|
||||
assert_equal("yo", r.read)
|
||||
assert_equal("ye", e.read)
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def test_disable_stderr
|
||||
with_pipe {|r, w|
|
||||
with_reopen(STDERR, w) {|old|
|
||||
w.close
|
||||
Open3.popen3(RUBY, '-e', 's=STDIN.read; STDOUT.print s+"o"; STDERR.print s+"e"', STDERR=>nil) {|i,o,t|
|
||||
assert_kind_of(Thread, t)
|
||||
i.print "y"
|
||||
i.close
|
||||
STDERR.reopen(old)
|
||||
assert_equal("yo", o.read)
|
||||
assert_equal("ye", r.read)
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def test_plug_pipe
|
||||
Open3.popen3(RUBY, '-e', 'STDOUT.print "1"') {|i1,o1,e1,t1|
|
||||
Open3.popen3(RUBY, '-e', 'STDOUT.print STDIN.read+"2"', STDIN=>o1) {|o2,e2,t2|
|
||||
assert_equal("12", o2.read)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def test_tree_pipe
|
||||
ia,oa,ea,ta = Open3.popen3(RUBY, '-e', 'i=STDIN.read; STDOUT.print i+"a"; STDERR.print i+"A"')
|
||||
ob,eb,tb = Open3.popen3(RUBY, '-e', 'i=STDIN.read; STDOUT.print i+"b"; STDERR.print i+"B"', STDIN=>oa)
|
||||
oc,ec,tc = Open3.popen3(RUBY, '-e', 'i=STDIN.read; STDOUT.print i+"c"; STDERR.print i+"C"', STDIN=>ob)
|
||||
od,ed,td = Open3.popen3(RUBY, '-e', 'i=STDIN.read; STDOUT.print i+"d"; STDERR.print i+"D"', STDIN=>eb)
|
||||
oe,ee,te = Open3.popen3(RUBY, '-e', 'i=STDIN.read; STDOUT.print i+"e"; STDERR.print i+"E"', STDIN=>ea)
|
||||
of,ef,tf = Open3.popen3(RUBY, '-e', 'i=STDIN.read; STDOUT.print i+"f"; STDERR.print i+"F"', STDIN=>oe)
|
||||
og,eg,tg = Open3.popen3(RUBY, '-e', 'i=STDIN.read; STDOUT.print i+"g"; STDERR.print i+"G"', STDIN=>ee)
|
||||
oa.close
|
||||
ea.close
|
||||
ob.close
|
||||
eb.close
|
||||
oe.close
|
||||
ee.close
|
||||
|
||||
ia.print "0"
|
||||
ia.close
|
||||
assert_equal("0abc", oc.read)
|
||||
assert_equal("0abC", ec.read)
|
||||
assert_equal("0aBd", od.read)
|
||||
assert_equal("0aBD", ed.read)
|
||||
assert_equal("0Aef", of.read)
|
||||
assert_equal("0AeF", ef.read)
|
||||
assert_equal("0AEg", og.read)
|
||||
assert_equal("0AEG", eg.read)
|
||||
ensure
|
||||
ia.close if !ia.closed?
|
||||
oa.close if !oa.closed?
|
||||
ea.close if !ea.closed?
|
||||
ob.close if !ob.closed?
|
||||
eb.close if !eb.closed?
|
||||
oc.close if !oc.closed?
|
||||
ec.close if !ec.closed?
|
||||
od.close if !od.closed?
|
||||
ed.close if !ed.closed?
|
||||
oe.close if !oe.closed?
|
||||
ee.close if !ee.closed?
|
||||
of.close if !of.closed?
|
||||
ef.close if !ef.closed?
|
||||
og.close if !og.closed?
|
||||
eg.close if !eg.closed?
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue