* lib/open3.rb (Open3.pipeline_start): new method.

(Open3.pipeline): ditto.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@20544 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
akr 2008-12-05 10:07:13 +00:00
Родитель 1385ce5f89
Коммит 415e2cbd88
3 изменённых файлов: 166 добавлений и 28 удалений

Просмотреть файл

@ -1,3 +1,8 @@
Fri Dec 5 19:06:04 2008 Tanaka Akira <akr@fsij.org>
* lib/open3.rb (Open3.pipeline_start): new method.
(Open3.pipeline): ditto.
Fri Dec 5 18:55:25 2008 Tanaka Akira <akr@fsij.org>
* process.c (run_exec_dup2): !save is false if Qnil.

Просмотреть файл

@ -18,9 +18,11 @@
# - Open3.poutput3 : give a string for stdin. get strings for stdout, stderr
# - Open3.poutput2 : give a string for stdin. get a string for stdout
# - Open3.poutput2e : give a string for stdin. get a string for merged stdout and stderr
# - Open3.pipeline_rw : pipes for stdin of the first and stdout of the last of a pipeline
# - Open3.pipeline_r : pipe for stdout of the last of a pipeline
# - Open3.pipeline_w : pipe for stdin of the first of a pipeline
# - Open3.pipeline_rw : pipes for first stdin and last stdout of a pipeline
# - Open3.pipeline_r : pipe for last stdout of a pipeline
# - Open3.pipeline_w : pipe for first stdin of a pipeline
# - Open3.pipeline_start : a pipeline
# - Open3.pipeline : run a pipline and wait
#
module Open3
@ -74,14 +76,14 @@ module Open3
end
in_r, in_w = IO.pipe
opts[STDIN] = in_r
opts[:in] = in_r
in_w.sync = true
out_r, out_w = IO.pipe
opts[STDOUT] = out_w
opts[:out] = out_w
err_r, err_w = IO.pipe
opts[STDERR] = err_w
opts[:err] = err_w
popen_run(cmd, opts, [in_r, out_w, err_w], [in_w, out_r, err_r], &block)
end
@ -105,6 +107,26 @@ module Open3
# stdin.close # stdin and stdout should be closed explicitly in this form.
# stdout.close
#
# Example:
#
# Open3.popen2("wc -c") {|i,o,t|
# i.print "answer to life the universe and everything"
# i.close
# p o.gets #=> "42\n"
# }
#
# Open3.popen2("bc -q") {|i,o,t|
# i.puts "obase=13"
# i.puts "6 * 9"
# p o.gets #=> "42\n"
# }
#
# Open3.popen2("dc") {|i,o,t|
# i.print "42P"
# i.close
# p o.read #=> "*"
# }
#
def popen2(*cmd, &block)
if Hash === cmd.last
opts = cmd.pop.dup
@ -113,11 +135,11 @@ module Open3
end
in_r, in_w = IO.pipe
opts[STDIN] = in_r
opts[:in] = in_r
in_w.sync = true
out_r, out_w = IO.pipe
opts[STDOUT] = out_w
opts[:out] = out_w
popen_run(cmd, opts, [in_r, out_w], [in_w, out_r], &block)
end
@ -149,11 +171,11 @@ module Open3
end
in_r, in_w = IO.pipe
opts[STDIN] = in_r
opts[:in] = in_r
in_w.sync = true
out_r, out_w = IO.pipe
opts[[STDOUT, STDERR]] = out_w
opts[[:out, :err]] = out_w
popen_run(cmd, opts, [in_r, out_w], [in_w, out_r], &block)
end
@ -189,6 +211,14 @@ module Open3
#
# Example:
#
# # dot is a command of graphviz.
# graph = <<'End'
# digraph g {
# a -> b
# }
# End
# layouted_graph, dot_log = Open3.poutput3("dot -v", :stdin_data=>graph)
#
# o, e, s = Open3.poutput3("echo a; sort >&2", :stdin_data=>"foo\nbar\nbaz\n")
# p o #=> "a\n"
# p e #=> "bar\nbaz\nfoo\n"
@ -221,6 +251,10 @@ module Open3
#
# If opts[:stdin_data] is specified, it is sent to the command's standard input.
#
# # factor is a command for integer factorization
# o, s = Open3.poutput2("factor", :stdin_data=>"42")
# p o #=> "42: 2 3 7\n"
#
def poutput2(*cmd, &block)
if Hash === cmd.last
opts = cmd.pop.dup
@ -286,6 +320,12 @@ module Open3
#
# Example:
#
# Open3.pipeline_rw("tr -dc A-Za-z", "wc -c") {|i,o,ts|
# i.puts "All persons more than a mile high to leave the court."
# i.close
# p o.gets #=> "42\n"
# }
#
# Open3.pipeline_rw("sort", "cat -n") {|stdin, stdout, wait_thrs|
# stdin.puts "foo"
# stdin.puts "bar"
@ -301,11 +341,11 @@ module Open3
end
in_r, in_w = IO.pipe
opts[STDIN] = in_r
opts[:in] = in_r
in_w.sync = true
out_r, out_w = IO.pipe
opts[STDOUT] = out_w
opts[:out] = out_w
pipeline_run(cmds, opts, [in_r, out_w], [in_w, out_r], &block)
end
@ -329,6 +369,14 @@ module Open3
# IO.copy_stream(r, STDOUT)
# }
#
# Open3.pipeline_r("zcat /var/log/apache2/access.log.*.gz",
# [{"LANG"=>"C"}, "grep", "GET /favicon.ico"],
# "logresolve") {|r, ts|
# r.each_line {|line|
# ...
# }
# }
#
# Open3.pipeline_r("yes", "head -10") {|r, ts|
# p r.read #=> "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"
# p ts[0].value #=> #<Process::Status: pid 24910 SIGPIPE (signal 13)>
@ -343,7 +391,7 @@ module Open3
end
out_r, out_w = IO.pipe
opts[STDOUT] = out_w
opts[:out] = out_w
pipeline_run(cmds, opts, [out_w], [out_r], &block)
end
@ -362,11 +410,8 @@ module Open3
#
# Example:
#
# Open3.pipeline_w("cat -n", "bzip2 -c", STDOUT=>"/tmp/z.bz2") {|w, ts|
# Open3.pipeline_w("bzip2 -c", :out=>"/tmp/hello.bz2") {|w, ts|
# w.puts "hello"
# w.close
# p ts[0].value
# p ts[1].value
# }
#
def pipeline_w(*cmds, &block)
@ -377,21 +422,74 @@ module Open3
end
in_r, in_w = IO.pipe
opts[STDIN] = in_r
opts[:in] = in_r
in_w.sync = true
pipeline_run(cmds, opts, [in_r], [in_w], &block)
end
module_function :pipeline_w
# Open3.pipeline_start starts list of commands as a pipeline.
# No pipe made for stdin of the first command and
# stdout of the last command.
#
# Open3.pipeline_start(cmd1, cmd2, ... [, opts]) {|wait_threads|
# ...
# }
#
# wait_threads = Open3.pipeline_start(cmd1, cmd2, ... [, opts])
# ...
#
def pipeline_start(*cmds, &block)
if Hash === cmds.last
opts = cmds.pop.dup
else
opts = {}
end
pipeline_run(cmds, opts, [], [], &block)
end
module_function :pipeline_start
# Open3.pipeline starts list of commands as a pipeline.
# It waits the finish of the commands.
# No pipe made for stdin of the first command and
# stdout of the last command.
#
# status_list = Open3.pipeline(cmd1, cmd2, ... [, opts])
#
# Example:
#
# fname = "/usr/share/man/man1/ruby.1.gz"
# p Open3.pipeline(["zcat", fname], "nroff -man", "less")
# #=> [#<Process::Status: pid 11817 exit 0>,
# # #<Process::Status: pid 11820 exit 0>,
# # #<Process::Status: pid 11828 exit 0>]
#
# # count lines
# Open3.pipeline("sort", "uniq -c", :in=>"names.txt", :out=>"count")
#
def pipeline(*cmds)
if Hash === cmds.last
opts = cmds.pop.dup
else
opts = {}
end
pipeline_run(cmds, opts, [], []) {|ts|
ts.map {|t| t.value }
}
end
module_function :pipeline
def pipeline_run(cmds, pipeline_opts, child_io, parent_io, &block) # :nodoc:
if cmds.empty?
raise ArgumentError, "no commands"
end
opts_base = pipeline_opts.dup
opts_base.delete STDIN
opts_base.delete STDOUT
opts_base.delete :in
opts_base.delete :out
wait_thrs = []
r = nil
@ -403,21 +501,21 @@ module Open3
cmd_opts.update cmd.pop if Hash === cmd.last
end
if i == 0
if !cmd_opts.include?(STDIN)
if pipeline_opts.include?(STDIN)
cmd_opts[STDIN] = pipeline_opts[STDIN]
if !cmd_opts.include?(:in)
if pipeline_opts.include?(:in)
cmd_opts[:in] = pipeline_opts[:in]
end
end
else
cmd_opts[STDIN] = r
cmd_opts[:in] = r
end
if i != cmds.length - 1
r2, w2 = IO.pipe
cmd_opts[STDOUT] = w2
cmd_opts[:out] = w2
else
if !cmd_opts.include?(STDOUT)
if pipeline_opts.include?(STDOUT)
cmd_opts[STDOUT] = pipeline_opts[STDOUT]
if !cmd_opts.include?(:out)
if pipeline_opts.include?(:out)
cmd_opts[:out] = pipeline_opts[:out]
end
end
end

Просмотреть файл

@ -196,4 +196,39 @@ class TestOpen3 < Test::Unit::TestCase
}
end
def test_pipeline_start
command = [RUBY, '-e', 's=STDIN.read; print s[1..-1]; exit s[0] == ?t']
str = 'ttftff'
Open3.pipeline_start([RUBY, '-e', 'print ARGV[0]', str],
*([command]*str.length)) {|ts|
assert_kind_of(Array, ts)
assert_equal(str.length+1, ts.length)
ts.each {|t| assert_kind_of(Thread, t) }
ts.each_with_index {|t, i|
if i == 0
assert(t.value.success?)
else
assert_equal(str[i-1] == ?t, t.value.success?)
end
}
}
end
def test_pipeline
command = [RUBY, '-e', 's=STDIN.read; print s[1..-1]; exit s[0] == ?t']
str = 'ttftff'
ss = Open3.pipeline([RUBY, '-e', 'print ARGV[0]', str],
*([command]*str.length))
assert_kind_of(Array, ss)
assert_equal(str.length+1, ss.length)
ss.each {|s| assert_kind_of(Process::Status, s) }
ss.each_with_index {|s, i|
if i == 0
assert(s.success?)
else
assert_equal(str[i-1] == ?t, s.success?)
end
}
end
end