open3_backport 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/.rvmrc +1 -0
- data/BSDL +23 -0
- data/Gemfile +4 -0
- data/Guardfile +13 -0
- data/LICENSE +65 -0
- data/README.md +228 -0
- data/Rakefile +4 -0
- data/lib/open3_backport.rb +6 -0
- data/lib/open3_backport/open3.rb +676 -0
- data/lib/open3_backport/original_open3.rb +729 -0
- data/lib/open3_backport/version.rb +3 -0
- data/open3_backport.gemspec +25 -0
- data/spec.opts +6 -0
- data/spec/lib/open3_spec.rb +198 -0
- data/spec/spec_helper.rb +6 -0
- data/tasks/console.rake +5 -0
- data/tasks/environment.rake +4 -0
- data/tasks/spec.rake +12 -0
- metadata +194 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm 1.8.7@open3_backport --create
|
data/BSDL
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (C) 1993-2010 Yukihiro Matsumoto. All rights reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
4
|
+
modification, are permitted provided that the following conditions
|
5
|
+
are met:
|
6
|
+
1. Redistributions of source code must retain the above copyright
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
8
|
+
2. Redistributions in binary form must reproduce the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer in the
|
10
|
+
documentation and/or other materials provided with the distribution.
|
11
|
+
|
12
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
13
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
14
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
15
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
16
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
17
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
18
|
+
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
19
|
+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
20
|
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
21
|
+
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
22
|
+
SUCH DAMAGE.
|
23
|
+
|
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# See https://github.com/guard/guard#readme
|
2
|
+
|
3
|
+
guard 'bundler' do
|
4
|
+
watch('Gemfile')
|
5
|
+
watch(%r{.+\.gemspec})
|
6
|
+
end
|
7
|
+
|
8
|
+
guard 'rspec', :cli => '-c --format documentation -r ./spec/spec_helper.rb' do
|
9
|
+
watch(%r{^spec/.+_spec\.rb})
|
10
|
+
watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
11
|
+
watch('spec/spec_helper.rb') { "spec/" }
|
12
|
+
watch('lib/open3_backport.rb') { "spec/" }
|
13
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
The important bits of open3_backport are pulled directly from Ruby source,
|
2
|
+
available at http://www.ruby-lang.org/. Copyrights of all other code in
|
3
|
+
the gem are assigned to the copyright owner of Ruby (Yukihiro Matsumoto).
|
4
|
+
|
5
|
+
This gem is available under the same license(s) as Ruby itself, reproduced
|
6
|
+
here for your convenience:
|
7
|
+
|
8
|
+
======
|
9
|
+
|
10
|
+
Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
|
11
|
+
You can redistribute it and/or modify it under either the terms of the
|
12
|
+
2-clause BSDL (see the file BSDL), or the conditions below:
|
13
|
+
|
14
|
+
1. You may make and give away verbatim copies of the source form of the
|
15
|
+
software without restriction, provided that you duplicate all of the
|
16
|
+
original copyright notices and associated disclaimers.
|
17
|
+
|
18
|
+
2. You may modify your copy of the software in any way, provided that
|
19
|
+
you do at least ONE of the following:
|
20
|
+
|
21
|
+
a) place your modifications in the Public Domain or otherwise
|
22
|
+
make them Freely Available, such as by posting said
|
23
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
24
|
+
the author to include your modifications in the software.
|
25
|
+
|
26
|
+
b) use the modified software only within your corporation or
|
27
|
+
organization.
|
28
|
+
|
29
|
+
c) give non-standard binaries non-standard names, with
|
30
|
+
instructions on where to get the original software distribution.
|
31
|
+
|
32
|
+
d) make other distribution arrangements with the author.
|
33
|
+
|
34
|
+
3. You may distribute the software in object code or binary form,
|
35
|
+
provided that you do at least ONE of the following:
|
36
|
+
|
37
|
+
a) distribute the binaries and library files of the software,
|
38
|
+
together with instructions (in the manual page or equivalent)
|
39
|
+
on where to get the original distribution.
|
40
|
+
|
41
|
+
b) accompany the distribution with the machine-readable source of
|
42
|
+
the software.
|
43
|
+
|
44
|
+
c) give non-standard binaries non-standard names, with
|
45
|
+
instructions on where to get the original software distribution.
|
46
|
+
|
47
|
+
d) make other distribution arrangements with the author.
|
48
|
+
|
49
|
+
4. You may modify and include the part of the software into any other
|
50
|
+
software (possibly commercial). But some files in the distribution
|
51
|
+
are not written by the author, so that they are not under these terms.
|
52
|
+
|
53
|
+
For the list of those files and their copying conditions, see the
|
54
|
+
file LEGAL.
|
55
|
+
|
56
|
+
5. The scripts and library files supplied as input to or produced as
|
57
|
+
output from the software do not automatically fall under the
|
58
|
+
copyright of the software, but belong to whomever generated them,
|
59
|
+
and may be sold commercially, and may be aggregated with this
|
60
|
+
software.
|
61
|
+
|
62
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
63
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
64
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
65
|
+
PURPOSE.
|
data/README.md
ADDED
@@ -0,0 +1,228 @@
|
|
1
|
+
# Open3Backport
|
2
|
+
|
3
|
+
Backport of Ruby 1.9's Open3 methods, for use in Ruby 1.8.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'open3_backport'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install open3_backport
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
For the most part, you can just use Open3 methods the same way you would in
|
22
|
+
Ruby 1.9. However, there is currently no support for setting environment nor
|
23
|
+
passing any of the special options that Process.spawn supports in Ruby 1.9.
|
24
|
+
|
25
|
+
Here are some examples that should work fine...
|
26
|
+
|
27
|
+
# Block form:
|
28
|
+
Open3.popen3("echo", "a") do |stdin, stdout, stderr, wait_thr|
|
29
|
+
pid = wait_thr.pid # pid of the started process.
|
30
|
+
exit_status = wait_thr.value # Process::Status object returned.
|
31
|
+
end
|
32
|
+
|
33
|
+
# Non-block form:
|
34
|
+
stdin, stdout, stderr, wait_thr = Open3.popen3("echo", "a")
|
35
|
+
pid = wait_thr[:pid] # pid of the started process.
|
36
|
+
stdin.close # stdin, stdout and stderr should be closed explicitly in this form.
|
37
|
+
stdout.close
|
38
|
+
stderr.close
|
39
|
+
exit_status = wait_thr.value # Process::Status object returned.
|
40
|
+
|
41
|
+
Open3.popen3("echo a") {|i, o, e, t| ... }
|
42
|
+
|
43
|
+
Open3.popen3("echo", "a") {|i, o, e, t| ... }
|
44
|
+
|
45
|
+
Open3.popen2("wc -c") do |i, o, t|
|
46
|
+
i.print "answer to life the universe and everything"
|
47
|
+
i.close
|
48
|
+
p o.gets #=> "42\n"
|
49
|
+
end
|
50
|
+
|
51
|
+
Open3.popen2("bc -q") do |i, o, t|
|
52
|
+
i.puts "obase=13"
|
53
|
+
i.puts "6 * 9"
|
54
|
+
p o.gets #=> "42\n"
|
55
|
+
end
|
56
|
+
|
57
|
+
Open3.popen2("dc") do |i, o, t|
|
58
|
+
i.print "42P"
|
59
|
+
i.close
|
60
|
+
p o.read #=> "*"
|
61
|
+
end
|
62
|
+
|
63
|
+
# dot is a command of graphviz.
|
64
|
+
graph = <<'End'
|
65
|
+
digraph g {
|
66
|
+
a -> b
|
67
|
+
}
|
68
|
+
End
|
69
|
+
layouted_graph, dot_log = Open3.capture3("dot -v", :stdin_data=>graph)
|
70
|
+
|
71
|
+
o, e, s = Open3.capture3("echo a; sort >&2", :stdin_data=>"foo\nbar\nbaz\n")
|
72
|
+
p o #=> "a\n"
|
73
|
+
p e #=> "bar\nbaz\nfoo\n"
|
74
|
+
p s #=> #<Process::Status: pid 32682 exit 0>
|
75
|
+
|
76
|
+
image = File.read("/usr/share/openclipart/png/animals/mammals/sheep-md-v0.1.png", :binmode=>true)
|
77
|
+
thumnail, err, s = Open3.capture3("convert -thumbnail 80 png:- png:-", :stdin_data=>image, :binmode=>true)
|
78
|
+
if s.success?
|
79
|
+
STDOUT.binmode
|
80
|
+
print thumnail
|
81
|
+
end
|
82
|
+
|
83
|
+
# factor is a command for integer factorization.
|
84
|
+
o, s = Open3.capture2("factor", :stdin_data=>"42")
|
85
|
+
p o #=> "42: 2 3 7\n"
|
86
|
+
|
87
|
+
# generate x**2 graph in png using gnuplot.
|
88
|
+
gnuplot_commands = <<"End"
|
89
|
+
set terminal png
|
90
|
+
plot x**2, "-" with lines
|
91
|
+
1 14
|
92
|
+
2 1
|
93
|
+
3 8
|
94
|
+
4 5
|
95
|
+
e
|
96
|
+
End
|
97
|
+
image, s = Open3.capture2("gnuplot", :stdin_data=>gnuplot_commands, :binmode=>true)
|
98
|
+
|
99
|
+
# capture make log
|
100
|
+
make_log, s = Open3.capture2e("make")
|
101
|
+
|
102
|
+
|
103
|
+
The following examples do not work yet. Pull requests are welcome!
|
104
|
+
|
105
|
+
source = "foo.c"
|
106
|
+
Open3.popen2e("gcc", "-Wall", source) do |i, oe, t|
|
107
|
+
oe.each do |line|
|
108
|
+
if /warning/ =~ line
|
109
|
+
# ...
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
Open3.pipeline_rw(["tr", "-dc", "A-Za-z"], ["wc", "-c"]) do |i, o, ts|
|
115
|
+
i.puts "All persons more than a mile high to leave the court."
|
116
|
+
i.close
|
117
|
+
p o.gets #=> "42\n"
|
118
|
+
end
|
119
|
+
|
120
|
+
Open3.pipeline_rw("sort", "cat -n") do |stdin, stdout, wait_thrs|
|
121
|
+
stdin.puts "foo"
|
122
|
+
stdin.puts "bar"
|
123
|
+
stdin.puts "baz"
|
124
|
+
stdin.close # send EOF to sort.
|
125
|
+
p stdout.read #=> " 1\tbar\n 2\tbaz\n 3\tfoo\n"
|
126
|
+
end
|
127
|
+
|
128
|
+
Open3.pipeline_r("zcat /var/log/apache2/access.log.*.gz",
|
129
|
+
[{"LANG"=>"C"}, "grep", "GET /favicon.ico"],
|
130
|
+
"logresolve") {|o, ts|
|
131
|
+
o.each_line {|line|
|
132
|
+
# ...
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
Open3.pipeline_r("yes", "head -10") {|o, ts|
|
137
|
+
p o.read #=> "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"
|
138
|
+
p ts[0].value #=> #<Process::Status: pid 24910 SIGPIPE (signal 13)>
|
139
|
+
p ts[1].value #=> #<Process::Status: pid 24913 exit 0>
|
140
|
+
}
|
141
|
+
|
142
|
+
Open3.pipeline_w("bzip2 -c", :out=>"/tmp/hello.bz2") {|i, ts|
|
143
|
+
i.puts "hello"
|
144
|
+
}
|
145
|
+
|
146
|
+
# run xeyes in 10 seconds.
|
147
|
+
Open3.pipeline_start("xeyes") {|ts|
|
148
|
+
sleep 10
|
149
|
+
t = ts[0]
|
150
|
+
Process.kill("TERM", t.pid)
|
151
|
+
p t.value #=> #<Process::Status: pid 911 SIGTERM (signal 15)>
|
152
|
+
}
|
153
|
+
|
154
|
+
# convert pdf to ps and send it to a printer.
|
155
|
+
# collect error message of pdftops and lpr.
|
156
|
+
pdf_file = "paper.pdf"
|
157
|
+
printer = "printer-name"
|
158
|
+
err_r, err_w = IO.pipe
|
159
|
+
Open3.pipeline_start(["pdftops", pdf_file, "-"],
|
160
|
+
["lpr", "-P#{printer}"],
|
161
|
+
:err=>err_w) {|ts|
|
162
|
+
err_w.close
|
163
|
+
p err_r.read # error messages of pdftops and lpr.
|
164
|
+
}
|
165
|
+
|
166
|
+
fname = "/usr/share/man/man1/ruby.1.gz"
|
167
|
+
p Open3.pipeline(["zcat", fname], "nroff -man", "less")
|
168
|
+
#=> [#<Process::Status: pid 11817 exit 0>,
|
169
|
+
# #<Process::Status: pid 11820 exit 0>,
|
170
|
+
# #<Process::Status: pid 11828 exit 0>]
|
171
|
+
|
172
|
+
fname = "/usr/share/man/man1/ls.1.gz"
|
173
|
+
Open3.pipeline(["zcat", fname], "nroff -man", "colcrt")
|
174
|
+
|
175
|
+
# convert PDF to PS and send to a printer by lpr
|
176
|
+
pdf_file = "paper.pdf"
|
177
|
+
printer = "printer-name"
|
178
|
+
Open3.pipeline(["pdftops", pdf_file, "-"],
|
179
|
+
["lpr", "-P#{printer}"])
|
180
|
+
|
181
|
+
# count lines
|
182
|
+
Open3.pipeline("sort", "uniq -c", :in=>"names.txt", :out=>"count")
|
183
|
+
|
184
|
+
# cyclic pipeline
|
185
|
+
r,w = IO.pipe
|
186
|
+
w.print "ibase=14\n10\n"
|
187
|
+
Open3.pipeline("bc", "tee /dev/tty", :in=>r, :out=>w)
|
188
|
+
#=> 14
|
189
|
+
# 18
|
190
|
+
# 22
|
191
|
+
# 30
|
192
|
+
# 42
|
193
|
+
# 58
|
194
|
+
# 78
|
195
|
+
# 106
|
196
|
+
# 202
|
197
|
+
|
198
|
+
## Version History
|
199
|
+
|
200
|
+
0.0.1 - Initial release. No support for setting environment nor
|
201
|
+
passing any of the special options that Process.spawn supports in Ruby 1.9.
|
202
|
+
These methods are not yet implemented: popen2e, pipeline, pipeline_start,
|
203
|
+
pipeline_r, pipeline_w, pipeline_rw.
|
204
|
+
|
205
|
+
## Credits
|
206
|
+
|
207
|
+
This gem was written by Chris Johnson for Crossroads Systems, Inc. The code
|
208
|
+
and documentation was copied from the Ruby 1.9.3 stdlib, and then key method
|
209
|
+
implementations were re-written and tested to work in Ruby 1.8.7, while aiming
|
210
|
+
to maintain maximum compatibility with the Ruby 1.9.3 method interfaces. The
|
211
|
+
replacement implementation makes heavy use of the open4 gem.
|
212
|
+
|
213
|
+
## License
|
214
|
+
|
215
|
+
The important bits of open3_backport are pulled directly from Ruby source,
|
216
|
+
available at http://www.ruby-lang.org/. Copyrights of all other code in
|
217
|
+
the gem are assigned to the copyright owner of Ruby (Yukihiro Matsumoto).
|
218
|
+
|
219
|
+
This gem is available under the same license(s) as Ruby itself (See LICENSE
|
220
|
+
file).
|
221
|
+
|
222
|
+
## Contributing
|
223
|
+
|
224
|
+
1. Fork it
|
225
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
226
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
227
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
228
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,676 @@
|
|
1
|
+
#
|
2
|
+
# = open3.rb: Popen, but with stderr, too
|
3
|
+
#
|
4
|
+
# Author:: Yukihiro Matsumoto, backport by Chris Johnson
|
5
|
+
# Documentation:: Konrad Meyer, backport by Chris Johnson
|
6
|
+
#
|
7
|
+
# Open3 gives you access to stdin, stdout, and stderr when running other
|
8
|
+
# programs.
|
9
|
+
#
|
10
|
+
|
11
|
+
#
|
12
|
+
# Open3 grants you access to stdin, stdout, stderr and a thread to wait the
|
13
|
+
# child process when running another program.
|
14
|
+
#
|
15
|
+
# - Open3.popen3 : pipes for stdin, stdout, stderr
|
16
|
+
# - Open3.popen2 : pipes for stdin, stdout
|
17
|
+
# - Open3.popen2e : pipes for stdin, merged stdout and stderr
|
18
|
+
# - Open3.capture3 : give a string for stdin. get strings for stdout, stderr
|
19
|
+
# - Open3.capture2 : give a string for stdin. get a string for stdout
|
20
|
+
# - Open3.capture2e : give a string for stdin. get a string for merged stdout and stderr
|
21
|
+
# - Open3.pipeline_rw : pipes for first stdin and last stdout of a pipeline
|
22
|
+
# - Open3.pipeline_r : pipe for last stdout of a pipeline
|
23
|
+
# - Open3.pipeline_w : pipe for first stdin of a pipeline
|
24
|
+
# - Open3.pipeline_start : a pipeline
|
25
|
+
# - Open3.pipeline : run a pipline and wait
|
26
|
+
#
|
27
|
+
|
28
|
+
module Open3
|
29
|
+
|
30
|
+
def detach(pid) # :nodoc:
|
31
|
+
thread = Process.detach(pid)
|
32
|
+
thread[:pid] = pid
|
33
|
+
def thread.pid
|
34
|
+
self[:pid]
|
35
|
+
end
|
36
|
+
thread
|
37
|
+
end
|
38
|
+
module_function :detach
|
39
|
+
class << self
|
40
|
+
private :detach
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Open stdin, stdout, and stderr streams and start external executable.
|
45
|
+
# In addition, a thread for waiting the started process is noticed.
|
46
|
+
# The thread has a pid method and thread variable :pid which is the pid of
|
47
|
+
# the started process.
|
48
|
+
#
|
49
|
+
# Block form:
|
50
|
+
#
|
51
|
+
# Open3.popen3(cmd...) {|stdin, stdout, stderr, wait_thr|
|
52
|
+
# pid = wait_thr.pid # pid of the started process.
|
53
|
+
# ...
|
54
|
+
# exit_status = wait_thr.value # Process::Status object returned.
|
55
|
+
# }
|
56
|
+
#
|
57
|
+
# Non-block form:
|
58
|
+
#
|
59
|
+
# stdin, stdout, stderr, wait_thr = Open3.popen3(cmd...)
|
60
|
+
# pid = wait_thr[:pid] # pid of the started process.
|
61
|
+
# ...
|
62
|
+
# stdin.close # stdin, stdout and stderr should be closed explicitly in this form.
|
63
|
+
# stdout.close
|
64
|
+
# stderr.close
|
65
|
+
# exit_status = wait_thr.value # Process::Status object returned.
|
66
|
+
#
|
67
|
+
# So a commandline string and list of argument strings can be accepted as follows.
|
68
|
+
#
|
69
|
+
# Open3.popen3("echo", "a") {|i, o, e, t| ... }
|
70
|
+
#
|
71
|
+
# wait_thr.value waits the termination of the process.
|
72
|
+
# The block form also waits the process when it returns.
|
73
|
+
#
|
74
|
+
# Closing stdin, stdout and stderr does not wait the process.
|
75
|
+
#
|
76
|
+
def popen3(*cmd)
|
77
|
+
if block_given?
|
78
|
+
begin
|
79
|
+
pid, stdin, stdout, stderr = Open4::popen4(*cmd)
|
80
|
+
wait_thr = detach(pid)
|
81
|
+
stdin.sync = true
|
82
|
+
return yield(stdin, stdout, stderr, wait_thr)
|
83
|
+
ensure
|
84
|
+
stdin.close unless stdin.nil? || stdin.closed?
|
85
|
+
stdout.close unless stdout.nil? || stdout.closed?
|
86
|
+
stderr.close unless stderr.nil? || stderr.closed?
|
87
|
+
wait_thr.value unless wait_thr.nil?
|
88
|
+
end
|
89
|
+
else
|
90
|
+
pid, stdin, stdout, stderr = Open4::popen4(*cmd)
|
91
|
+
stdin.sync = true
|
92
|
+
return [stdin, stdout, stderr, detach(pid)]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
module_function :popen3
|
96
|
+
|
97
|
+
# Open3.popen2 is similer to Open3.popen3 except it doesn't make a pipe for
|
98
|
+
# the standard error stream.
|
99
|
+
#
|
100
|
+
# Block form:
|
101
|
+
#
|
102
|
+
# Open3.popen2(cmd...) {|stdin, stdout, wait_thr|
|
103
|
+
# pid = wait_thr.pid # pid of the started process.
|
104
|
+
# ...
|
105
|
+
# exit_status = wait_thr.value # Process::Status object returned.
|
106
|
+
# }
|
107
|
+
#
|
108
|
+
# Non-block form:
|
109
|
+
#
|
110
|
+
# stdin, stdout, wait_thr = Open3.popen2(cmd...)
|
111
|
+
# ...
|
112
|
+
# stdin.close # stdin and stdout should be closed explicitly in this form.
|
113
|
+
# stdout.close
|
114
|
+
#
|
115
|
+
# Example:
|
116
|
+
#
|
117
|
+
# Open3.popen2("wc -c") {|i,o,t|
|
118
|
+
# i.print "answer to life the universe and everything"
|
119
|
+
# i.close
|
120
|
+
# p o.gets #=> "42\n"
|
121
|
+
# }
|
122
|
+
#
|
123
|
+
# Open3.popen2("bc -q") {|i,o,t|
|
124
|
+
# i.puts "obase=13"
|
125
|
+
# i.puts "6 * 9"
|
126
|
+
# p o.gets #=> "42\n"
|
127
|
+
# }
|
128
|
+
#
|
129
|
+
# Open3.popen2("dc") {|i,o,t|
|
130
|
+
# i.print "42P"
|
131
|
+
# i.close
|
132
|
+
# p o.read #=> "*"
|
133
|
+
# }
|
134
|
+
#
|
135
|
+
def popen2(*cmd)
|
136
|
+
if block_given?
|
137
|
+
popen3(*cmd) do |i, o, e, t|
|
138
|
+
e.close
|
139
|
+
yield(i, o, t)
|
140
|
+
end
|
141
|
+
else
|
142
|
+
i, o, e, t = popen3(*cmd)
|
143
|
+
e.close
|
144
|
+
return [i, o, t]
|
145
|
+
end
|
146
|
+
end
|
147
|
+
module_function :popen2
|
148
|
+
|
149
|
+
# Open3.popen2e is similer to Open3.popen3 except it merges
|
150
|
+
# the standard output stream and the standard error stream.
|
151
|
+
#
|
152
|
+
# Block form:
|
153
|
+
#
|
154
|
+
# Open3.popen2e(cmd...) {|stdin, stdout_and_stderr, wait_thr|
|
155
|
+
# pid = wait_thr.pid # pid of the started process.
|
156
|
+
# ...
|
157
|
+
# exit_status = wait_thr.value # Process::Status object returned.
|
158
|
+
# }
|
159
|
+
#
|
160
|
+
# Non-block form:
|
161
|
+
#
|
162
|
+
# stdin, stdout_and_stderr, wait_thr = Open3.popen2e(cmd...)
|
163
|
+
# ...
|
164
|
+
# stdin.close # stdin and stdout_and_stderr should be closed explicitly in this form.
|
165
|
+
# stdout_and_stderr.close
|
166
|
+
#
|
167
|
+
# Example:
|
168
|
+
# # check gcc warnings
|
169
|
+
# source = "foo.c"
|
170
|
+
# Open3.popen2e("gcc", "-Wall", source) {|i,oe,t|
|
171
|
+
# oe.each {|line|
|
172
|
+
# if /warning/ =~ line
|
173
|
+
# ...
|
174
|
+
# end
|
175
|
+
# }
|
176
|
+
# }
|
177
|
+
#
|
178
|
+
def popen2e(*cmd)
|
179
|
+
if block_given?
|
180
|
+
popen3(*cmd) do |i, o, e, t|
|
181
|
+
yield(i, merged_read_stream(o, e), t)
|
182
|
+
end
|
183
|
+
else
|
184
|
+
i, o, e, t = popen3(*cmd)
|
185
|
+
return [i, merged_read_stream(o, e), t]
|
186
|
+
end
|
187
|
+
end
|
188
|
+
module_function :popen2e
|
189
|
+
|
190
|
+
def merged_read_stream(*streams) # :nodoc:
|
191
|
+
raise NotImplementedError
|
192
|
+
end
|
193
|
+
|
194
|
+
# Open3.capture3 captures the standard output and the standard error of a command.
|
195
|
+
#
|
196
|
+
# stdout_str, stderr_str, status = Open3.capture3(cmd... [, opts])
|
197
|
+
#
|
198
|
+
# The cmd arguments are passed to Open3.popen3.
|
199
|
+
#
|
200
|
+
# If opts[:stdin_data] is specified, it is sent to the command's standard input.
|
201
|
+
#
|
202
|
+
# If opts[:binmode] is true, internal pipes are set to binary mode.
|
203
|
+
#
|
204
|
+
# Example:
|
205
|
+
#
|
206
|
+
# # dot is a command of graphviz.
|
207
|
+
# graph = <<'End'
|
208
|
+
# digraph g {
|
209
|
+
# a -> b
|
210
|
+
# }
|
211
|
+
# End
|
212
|
+
# layouted_graph, dot_log = Open3.capture3("dot -v", :stdin_data=>graph)
|
213
|
+
#
|
214
|
+
# o, e, s = Open3.capture3("echo a; sort >&2", :stdin_data=>"foo\nbar\nbaz\n")
|
215
|
+
# p o #=> "a\n"
|
216
|
+
# p e #=> "bar\nbaz\nfoo\n"
|
217
|
+
# p s #=> #<Process::Status: pid 32682 exit 0>
|
218
|
+
#
|
219
|
+
# # generate a thumnail image using the convert command of ImageMagick.
|
220
|
+
# # However, if the image stored really in a file,
|
221
|
+
# # system("convert", "-thumbnail", "80", "png:#{filename}", "png:-") is better
|
222
|
+
# # because memory consumption.
|
223
|
+
# # But if the image is stored in a DB or generated by gnuplot Open3.capture2 example,
|
224
|
+
# # Open3.capture3 is considerable.
|
225
|
+
# #
|
226
|
+
# image = File.read("/usr/share/openclipart/png/animals/mammals/sheep-md-v0.1.png", :binmode=>true)
|
227
|
+
# thumnail, err, s = Open3.capture3("convert -thumbnail 80 png:- png:-", :stdin_data=>image, :binmode=>true)
|
228
|
+
# if s.success?
|
229
|
+
# STDOUT.binmode; print thumnail
|
230
|
+
# end
|
231
|
+
#
|
232
|
+
def capture3(*cmd)
|
233
|
+
if Hash === cmd.last
|
234
|
+
opts = cmd.pop.dup
|
235
|
+
else
|
236
|
+
opts = {}
|
237
|
+
end
|
238
|
+
|
239
|
+
binmode = opts[:binmode]
|
240
|
+
i_data = (opts[:stdin_data] || '').to_s
|
241
|
+
o_data = ''
|
242
|
+
e_data = ''
|
243
|
+
|
244
|
+
popen3(*cmd) do |i, o, e, t|
|
245
|
+
if binmode
|
246
|
+
i.binmode
|
247
|
+
o.binmode
|
248
|
+
e.binmode
|
249
|
+
end
|
250
|
+
|
251
|
+
i_complete = i_data.empty?
|
252
|
+
o_complete = false
|
253
|
+
e_complete = false
|
254
|
+
|
255
|
+
until i_complete && o_complete && e_complete
|
256
|
+
i_blocked = false
|
257
|
+
o_blocked = false
|
258
|
+
e_blocked = false
|
259
|
+
|
260
|
+
unless i_complete
|
261
|
+
begin
|
262
|
+
bytes_written = i.write_nonblock(i_data)
|
263
|
+
if bytes_written == i_data.length
|
264
|
+
i.close
|
265
|
+
i_complete = true
|
266
|
+
else
|
267
|
+
i_data = i_data[bytes_written .. -1]
|
268
|
+
end
|
269
|
+
rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR
|
270
|
+
i_blocked = true
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
unless o_complete
|
275
|
+
begin
|
276
|
+
o_data << o.read_nonblock(CAPTURE_BUFFER_SIZE)
|
277
|
+
rescue Errno::EWOULDBLOCK, Errno::EAGAIN
|
278
|
+
o_blocked = true
|
279
|
+
rescue EOFError
|
280
|
+
raise unless i_complete
|
281
|
+
o.close
|
282
|
+
o_complete = true
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
unless e_complete
|
287
|
+
begin
|
288
|
+
e_data << e.read_nonblock(CAPTURE_BUFFER_SIZE)
|
289
|
+
rescue Errno::EWOULDBLOCK, Errno::EAGAIN
|
290
|
+
e_blocked = true
|
291
|
+
rescue EOFError
|
292
|
+
raise unless i_complete
|
293
|
+
e.close
|
294
|
+
e_complete = true
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
if i_blocked && o_blocked && e_blocked
|
299
|
+
IO.select([o, e], [i], [o,e,i])
|
300
|
+
end
|
301
|
+
end
|
302
|
+
return [o_data, e_data, t.value]
|
303
|
+
end
|
304
|
+
end
|
305
|
+
module_function :capture3
|
306
|
+
|
307
|
+
# Open3.capture2 captures the standard output of a command.
|
308
|
+
#
|
309
|
+
# stdout_str, status = Open3.capture2(cmd... [, opts])
|
310
|
+
#
|
311
|
+
# The cmd arguments are passed to Open3.popen3.
|
312
|
+
#
|
313
|
+
# If opts[:stdin_data] is specified, it is sent to the command's standard input.
|
314
|
+
#
|
315
|
+
# If opts[:binmode] is true, internal pipes are set to binary mode.
|
316
|
+
#
|
317
|
+
# Example:
|
318
|
+
#
|
319
|
+
# # factor is a command for integer factorization.
|
320
|
+
# o, s = Open3.capture2("factor", :stdin_data=>"42")
|
321
|
+
# p o #=> "42: 2 3 7\n"
|
322
|
+
#
|
323
|
+
# # generate x**2 graph in png using gnuplot.
|
324
|
+
# gnuplot_commands = <<"End"
|
325
|
+
# set terminal png
|
326
|
+
# plot x**2, "-" with lines
|
327
|
+
# 1 14
|
328
|
+
# 2 1
|
329
|
+
# 3 8
|
330
|
+
# 4 5
|
331
|
+
# e
|
332
|
+
# End
|
333
|
+
# image, s = Open3.capture2("gnuplot", :stdin_data=>gnuplot_commands, :binmode=>true)
|
334
|
+
#
|
335
|
+
def capture2(*cmd)
|
336
|
+
if Hash === cmd.last
|
337
|
+
opts = cmd.pop.dup
|
338
|
+
else
|
339
|
+
opts = {}
|
340
|
+
end
|
341
|
+
|
342
|
+
binmode = opts[:binmode]
|
343
|
+
i_data = (opts[:stdin_data] || '').to_s
|
344
|
+
o_data = ''
|
345
|
+
|
346
|
+
popen3(*cmd) do |i, o, e, t|
|
347
|
+
e.close
|
348
|
+
if binmode
|
349
|
+
i.binmode
|
350
|
+
o.binmode
|
351
|
+
e.binmode
|
352
|
+
end
|
353
|
+
|
354
|
+
i_complete = i_data.empty?
|
355
|
+
o_complete = false
|
356
|
+
|
357
|
+
until i_complete && o_complete
|
358
|
+
i_blocked = false
|
359
|
+
o_blocked = false
|
360
|
+
|
361
|
+
unless i_complete
|
362
|
+
begin
|
363
|
+
bytes_written = i.write_nonblock(i_data)
|
364
|
+
if bytes_written == i_data.length
|
365
|
+
i.close
|
366
|
+
i_complete = true
|
367
|
+
else
|
368
|
+
i_data = i_data[bytes_written .. -1]
|
369
|
+
end
|
370
|
+
rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR
|
371
|
+
i_blocked = true
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
unless o_complete
|
376
|
+
begin
|
377
|
+
o_data << o.read_nonblock(CAPTURE_BUFFER_SIZE)
|
378
|
+
rescue Errno::EWOULDBLOCK, Errno::EAGAIN
|
379
|
+
o_blocked = true
|
380
|
+
rescue EOFError
|
381
|
+
raise unless i_complete
|
382
|
+
o.close
|
383
|
+
o_complete = true
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
if i_blocked && o_blocked && e_blocked
|
388
|
+
IO.select([o], [i], [o,i])
|
389
|
+
end
|
390
|
+
end
|
391
|
+
return [o_data, t.value]
|
392
|
+
end
|
393
|
+
end
|
394
|
+
module_function :capture2
|
395
|
+
|
396
|
+
# Open3.capture2e captures the standard output and the standard error of a command.
|
397
|
+
#
|
398
|
+
# stdout_and_stderr_str, status = Open3.capture2e(cmd... [, opts])
|
399
|
+
#
|
400
|
+
# The cmd arguments are passed to Open3.popen3.
|
401
|
+
#
|
402
|
+
# If opts[:stdin_data] is specified, it is sent to the command's standard input.
|
403
|
+
#
|
404
|
+
# If opts[:binmode] is true, internal pipes are set to binary mode.
|
405
|
+
#
|
406
|
+
# Example:
|
407
|
+
#
|
408
|
+
# # capture make log
|
409
|
+
# make_log, s = Open3.capture2e("make")
|
410
|
+
#
|
411
|
+
def capture2e(*cmd)
|
412
|
+
if Hash === cmd.last
|
413
|
+
opts = cmd.pop.dup
|
414
|
+
else
|
415
|
+
opts = {}
|
416
|
+
end
|
417
|
+
|
418
|
+
binmode = opts[:binmode]
|
419
|
+
i_data = (opts[:stdin_data] || '').to_s
|
420
|
+
oe_data = ''
|
421
|
+
|
422
|
+
popen3(*cmd) do |i, o, e, t|
|
423
|
+
if binmode
|
424
|
+
i.binmode
|
425
|
+
o.binmode
|
426
|
+
e.binmode
|
427
|
+
end
|
428
|
+
|
429
|
+
i_complete = i_data.empty?
|
430
|
+
o_complete = false
|
431
|
+
e_complete = false
|
432
|
+
|
433
|
+
until i_complete && o_complete && e_complete
|
434
|
+
i_blocked = false
|
435
|
+
o_blocked = false
|
436
|
+
e_blocked = false
|
437
|
+
|
438
|
+
unless i_complete
|
439
|
+
begin
|
440
|
+
bytes_written = i.write_nonblock(i_data)
|
441
|
+
if bytes_written == i_data.length
|
442
|
+
i.close
|
443
|
+
i_complete = true
|
444
|
+
else
|
445
|
+
i_data = i_data[bytes_written .. -1]
|
446
|
+
end
|
447
|
+
rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR
|
448
|
+
i_blocked = true
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
unless o_complete
|
453
|
+
begin
|
454
|
+
oe_data << o.read_nonblock(CAPTURE_BUFFER_SIZE)
|
455
|
+
rescue Errno::EWOULDBLOCK, Errno::EAGAIN
|
456
|
+
o_blocked = true
|
457
|
+
rescue EOFError
|
458
|
+
raise unless i_complete
|
459
|
+
o.close
|
460
|
+
o_complete = true
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
unless e_complete
|
465
|
+
begin
|
466
|
+
oe_data << e.read_nonblock(CAPTURE_BUFFER_SIZE)
|
467
|
+
rescue Errno::EWOULDBLOCK, Errno::EAGAIN
|
468
|
+
e_blocked = true
|
469
|
+
rescue EOFError
|
470
|
+
raise unless i_complete
|
471
|
+
e.close
|
472
|
+
e_complete = true
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
if i_blocked && o_blocked && e_blocked
|
477
|
+
IO.select([o, e], [i], [o,e,i])
|
478
|
+
end
|
479
|
+
end
|
480
|
+
return [oe_data, t.value]
|
481
|
+
end
|
482
|
+
end
|
483
|
+
module_function :capture2e
|
484
|
+
|
485
|
+
CAPTURE_BUFFER_SIZE = 65536
|
486
|
+
|
487
|
+
# Open3.pipeline_rw starts a list of commands as a pipeline with pipes
|
488
|
+
# which connects stdin of the first command and stdout of the last command.
|
489
|
+
#
|
490
|
+
# Open3.pipeline_rw(cmd1, cmd2, ...) {|first_stdin, last_stdout, wait_threads|
|
491
|
+
# ...
|
492
|
+
# }
|
493
|
+
#
|
494
|
+
# first_stdin, last_stdout, wait_threads = Open3.pipeline_rw(cmd1, cmd2, ...)
|
495
|
+
# ...
|
496
|
+
# first_stdin.close
|
497
|
+
# last_stdout.close
|
498
|
+
#
|
499
|
+
# Each cmd is a string or an array.
|
500
|
+
# If it is an array, the first element is the command name, and the remaining
|
501
|
+
# elements are arguments passed (without parsing) to the command.
|
502
|
+
#
|
503
|
+
# Example:
|
504
|
+
#
|
505
|
+
# Open3.pipeline_rw(["tr", "-dc", "A-Za-z"], ["wc", "-c"]) {|i,o,ts|
|
506
|
+
# i.puts "All persons more than a mile high to leave the court."
|
507
|
+
# i.close
|
508
|
+
# p o.gets #=> "42\n"
|
509
|
+
# }
|
510
|
+
#
|
511
|
+
# Open3.pipeline_rw("sort", "cat -n") {|stdin, stdout, wait_thrs|
|
512
|
+
# stdin.puts "foo"
|
513
|
+
# stdin.puts "bar"
|
514
|
+
# stdin.puts "baz"
|
515
|
+
# stdin.close # send EOF to sort.
|
516
|
+
# p stdout.read #=> " 1\tbar\n 2\tbaz\n 3\tfoo\n"
|
517
|
+
# }
|
518
|
+
def pipeline_rw(*cmds, &block)
|
519
|
+
raise NotImplementedError
|
520
|
+
end
|
521
|
+
module_function :pipeline_rw
|
522
|
+
|
523
|
+
# Open3.pipeline_r starts a list of commands as a pipeline with a pipe
|
524
|
+
# which connects stdout of the last command.
|
525
|
+
#
|
526
|
+
# Open3.pipeline_r(cmd1, cmd2, ...) {|last_stdout, wait_threads|
|
527
|
+
# ...
|
528
|
+
# }
|
529
|
+
#
|
530
|
+
# last_stdout, wait_threads = Open3.pipeline_r(cmd1, cmd2, ...)
|
531
|
+
# ...
|
532
|
+
# last_stdout.close
|
533
|
+
#
|
534
|
+
# Each cmd is a string or an array.
|
535
|
+
# If it is an array, the first element is the command name, and the remaining
|
536
|
+
# elements are arguments passed (without parsing) to the command.
|
537
|
+
#
|
538
|
+
# Example:
|
539
|
+
#
|
540
|
+
# Open3.pipeline_r("zcat /var/log/apache2/access.log.*.gz",
|
541
|
+
# [{"LANG"=>"C"}, "grep", "GET /favicon.ico"],
|
542
|
+
# "logresolve") {|o, ts|
|
543
|
+
# o.each_line {|line|
|
544
|
+
# ...
|
545
|
+
# }
|
546
|
+
# }
|
547
|
+
#
|
548
|
+
# Open3.pipeline_r("yes", "head -10") {|o, ts|
|
549
|
+
# p o.read #=> "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"
|
550
|
+
# p ts[0].value #=> #<Process::Status: pid 24910 SIGPIPE (signal 13)>
|
551
|
+
# p ts[1].value #=> #<Process::Status: pid 24913 exit 0>
|
552
|
+
# }
|
553
|
+
#
|
554
|
+
def pipeline_r(*cmds, &block)
|
555
|
+
raise NotImplementedError
|
556
|
+
end
|
557
|
+
module_function :pipeline_r
|
558
|
+
|
559
|
+
# Open3.pipeline_w starts a list of commands as a pipeline with a pipe
|
560
|
+
# which connects stdin of the first command.
|
561
|
+
#
|
562
|
+
# Open3.pipeline_w(cmd1, cmd2, ...) {|first_stdin, wait_threads|
|
563
|
+
# ...
|
564
|
+
# }
|
565
|
+
#
|
566
|
+
# first_stdin, wait_threads = Open3.pipeline_w(cmd1, cmd2, ...)
|
567
|
+
# ...
|
568
|
+
# first_stdin.close
|
569
|
+
#
|
570
|
+
# Each cmd is a string or an array.
|
571
|
+
# If it is an array, the first element is the command name, and the remaining
|
572
|
+
# elements are arguments passed (without parsing) to the command.
|
573
|
+
#
|
574
|
+
# Example:
|
575
|
+
#
|
576
|
+
# Open3.pipeline_w("bzip2 -c", :out=>"/tmp/hello.bz2") {|i, ts|
|
577
|
+
# i.puts "hello"
|
578
|
+
# }
|
579
|
+
#
|
580
|
+
def pipeline_w(*cmds, &block)
|
581
|
+
raise NotImplementedError
|
582
|
+
end
|
583
|
+
module_function :pipeline_w
|
584
|
+
|
585
|
+
# Open3.pipeline_start starts a list of commands as a pipeline.
|
586
|
+
# No pipe made for stdin of the first command and
|
587
|
+
# stdout of the last command.
|
588
|
+
#
|
589
|
+
# Open3.pipeline_start(cmd1, cmd2, ...) {|wait_threads|
|
590
|
+
# ...
|
591
|
+
# }
|
592
|
+
#
|
593
|
+
# wait_threads = Open3.pipeline_start(cmd1, cmd2, ...)
|
594
|
+
# ...
|
595
|
+
#
|
596
|
+
# Each cmd is a string or an array.
|
597
|
+
# If it is an array, the first element is the command name, and the remaining
|
598
|
+
# elements are arguments passed (without parsing) to the command.
|
599
|
+
#
|
600
|
+
# Example:
|
601
|
+
#
|
602
|
+
# # run xeyes in 10 seconds.
|
603
|
+
# Open3.pipeline_start("xeyes") {|ts|
|
604
|
+
# sleep 10
|
605
|
+
# t = ts[0]
|
606
|
+
# Process.kill("TERM", t.pid)
|
607
|
+
# p t.value #=> #<Process::Status: pid 911 SIGTERM (signal 15)>
|
608
|
+
# }
|
609
|
+
#
|
610
|
+
# # convert pdf to ps and send it to a printer.
|
611
|
+
# # collect error message of pdftops and lpr.
|
612
|
+
# pdf_file = "paper.pdf"
|
613
|
+
# printer = "printer-name"
|
614
|
+
# err_r, err_w = IO.pipe
|
615
|
+
# Open3.pipeline_start(["pdftops", pdf_file, "-"],
|
616
|
+
# ["lpr", "-P#{printer}"],
|
617
|
+
# :err=>err_w) {|ts|
|
618
|
+
# err_w.close
|
619
|
+
# p err_r.read # error messages of pdftops and lpr.
|
620
|
+
# }
|
621
|
+
#
|
622
|
+
def pipeline_start(*cmds, &block)
|
623
|
+
raise NotImplementedError
|
624
|
+
end
|
625
|
+
module_function :pipeline_start
|
626
|
+
|
627
|
+
# Open3.pipeline starts a list of commands as a pipeline.
|
628
|
+
# It waits the finish of the commands.
|
629
|
+
# No pipe made for stdin of the first command and
|
630
|
+
# stdout of the last command.
|
631
|
+
#
|
632
|
+
# status_list = Open3.pipeline(cmd1, cmd2, ...)
|
633
|
+
#
|
634
|
+
# Each cmd is a string or an array.
|
635
|
+
# If it is an array, the first element is the command name, and the remaining
|
636
|
+
# elements are arguments passed (without parsing) to the command.
|
637
|
+
#
|
638
|
+
# Example:
|
639
|
+
#
|
640
|
+
# fname = "/usr/share/man/man1/ruby.1.gz"
|
641
|
+
# p Open3.pipeline(["zcat", fname], "nroff -man", "less")
|
642
|
+
# #=> [#<Process::Status: pid 11817 exit 0>,
|
643
|
+
# # #<Process::Status: pid 11820 exit 0>,
|
644
|
+
# # #<Process::Status: pid 11828 exit 0>]
|
645
|
+
#
|
646
|
+
# fname = "/usr/share/man/man1/ls.1.gz"
|
647
|
+
# Open3.pipeline(["zcat", fname], "nroff -man", "colcrt")
|
648
|
+
#
|
649
|
+
# # convert PDF to PS and send to a printer by lpr
|
650
|
+
# pdf_file = "paper.pdf"
|
651
|
+
# printer = "printer-name"
|
652
|
+
# Open3.pipeline(["pdftops", pdf_file, "-"],
|
653
|
+
# ["lpr", "-P#{printer}"])
|
654
|
+
#
|
655
|
+
# # count lines
|
656
|
+
# Open3.pipeline("sort", "uniq -c", :in=>"names.txt", :out=>"count")
|
657
|
+
#
|
658
|
+
# # cyclic pipeline
|
659
|
+
# r,w = IO.pipe
|
660
|
+
# w.print "ibase=14\n10\n"
|
661
|
+
# Open3.pipeline("bc", "tee /dev/tty", :in=>r, :out=>w)
|
662
|
+
# #=> 14
|
663
|
+
# # 18
|
664
|
+
# # 22
|
665
|
+
# # 30
|
666
|
+
# # 42
|
667
|
+
# # 58
|
668
|
+
# # 78
|
669
|
+
# # 106
|
670
|
+
# # 202
|
671
|
+
#
|
672
|
+
def pipeline(*cmds)
|
673
|
+
raise NotImplementedError
|
674
|
+
end
|
675
|
+
module_function :pipeline
|
676
|
+
end
|