rubysl-open3 1.0.0 → 2.0.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: be17e7d953578c551dd3166577d08fc16db550eb
4
- data.tar.gz: 4d441dbd3cb04edef32a648990ee884caabcd9e6
3
+ metadata.gz: 4725b643894f56b7340f3d26dc6b531d48747062
4
+ data.tar.gz: 569740b4830701974d737047eb11b13ec5c4fbaa
5
5
  SHA512:
6
- metadata.gz: 80c18721011815e0bbeffe3832320fcf7e05a3003f85df37a48aeed063918e046e86c425ee4132e56350fbe0d6b72ba37824aa76bbe9b0945890f68464abe85a
7
- data.tar.gz: 14e6a51ecd038dd1217bfd562b98fa80e75ac6d0aa49f0454af60b34f9ddc66d1b61d78e612ea547f36ce230520330094b021401a76b95e36b5f4f03df7b550a
6
+ metadata.gz: 76f5c160264f878ae500ec33dd22b8f40c6a508138e1c207479764198c9f0884a17436219b84d84293f6b5a3263b2374ac98cb0b4073d4d5e972090f549c1ddf
7
+ data.tar.gz: aff8097cf834f544cecf5b627290b4e00f9f31222de3440012c7ab15cd71dbbc6aeca4df9ac312cdb7b14c0afb18c6fb162c678999a4c40bcfae48d5a9cc681b
@@ -1,8 +1,7 @@
1
1
  language: ruby
2
- before_install:
3
- - gem update --system
4
- - gem --version
5
- - gem install rubysl-bundler
6
- script: bundle exec mspec spec
2
+ env:
3
+ - RUBYLIB=lib
4
+ script: bundle exec mspec
7
5
  rvm:
8
- - rbx-nightly-18mode
6
+ - 1.9.3
7
+ - rbx-nightly-19mode
@@ -9,85 +9,710 @@
9
9
  #
10
10
 
11
11
  #
12
- # Open3 grants you access to stdin, stdout, and stderr when running another
13
- # program. Example:
12
+ # Open3 grants you access to stdin, stdout, stderr and a thread to wait the
13
+ # child process when running another program.
14
+ # You can specify various attributes, redirections, current directory, etc., of
15
+ # the program as Process.spawn.
14
16
  #
15
- # require "open3"
16
- # include Open3
17
- #
18
- # stdin, stdout, stderr = popen3('nroff -man')
19
- #
20
- # Open3.popen3 can also take a block which will receive stdin, stdout and
21
- # stderr as parameters. This ensures stdin, stdout and stderr are closed
22
- # once the block exits. Example:
23
- #
24
- # require "open3"
25
- #
26
- # Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }
17
+ # - Open3.popen3 : pipes for stdin, stdout, stderr
18
+ # - Open3.popen2 : pipes for stdin, stdout
19
+ # - Open3.popen2e : pipes for stdin, merged stdout and stderr
20
+ # - Open3.capture3 : give a string for stdin. get strings for stdout, stderr
21
+ # - Open3.capture2 : give a string for stdin. get a string for stdout
22
+ # - Open3.capture2e : give a string for stdin. get a string for merged stdout and stderr
23
+ # - Open3.pipeline_rw : pipes for first stdin and last stdout of a pipeline
24
+ # - Open3.pipeline_r : pipe for last stdout of a pipeline
25
+ # - Open3.pipeline_w : pipe for first stdin of a pipeline
26
+ # - Open3.pipeline_start : a pipeline
27
+ # - Open3.pipeline : run a pipline and wait
27
28
  #
28
29
 
29
30
  module Open3
30
- #
31
+
31
32
  # Open stdin, stdout, and stderr streams and start external executable.
33
+ # In addition, a thread for waiting the started process is noticed.
34
+ # The thread has a pid method and thread variable :pid which is the pid of
35
+ # the started process.
36
+ #
37
+ # Block form:
38
+ #
39
+ # Open3.popen3([env,] cmd... [, opts]) {|stdin, stdout, stderr, wait_thr|
40
+ # pid = wait_thr.pid # pid of the started process.
41
+ # ...
42
+ # exit_status = wait_thr.value # Process::Status object returned.
43
+ # }
44
+ #
45
+ # Non-block form:
46
+ #
47
+ # stdin, stdout, stderr, wait_thr = Open3.popen3([env,] cmd... [, opts])
48
+ # pid = wait_thr[:pid] # pid of the started process.
49
+ # ...
50
+ # stdin.close # stdin, stdout and stderr should be closed explicitly in this form.
51
+ # stdout.close
52
+ # stderr.close
53
+ # exit_status = wait_thr.value # Process::Status object returned.
54
+ #
55
+ # The parameters +cmd...+ is passed to Process.spawn.
56
+ # So a commandline string and list of argument strings can be accepted as follows.
57
+ #
58
+ # Open3.popen3("echo a") {|i, o, e, t| ... }
59
+ # Open3.popen3("echo", "a") {|i, o, e, t| ... }
60
+ # Open3.popen3(["echo", "argv0"], "a") {|i, o, e, t| ... }
61
+ #
62
+ # If the last parameter, opts, is a Hash, it is recognized as an option for Process.spawn.
63
+ #
64
+ # Open3.popen3("pwd", :chdir=>"/") {|i,o,e,t|
65
+ # p o.read.chomp #=> "/"
66
+ # }
67
+ #
68
+ # wait_thr.value waits the termination of the process.
69
+ # The block form also waits the process when it returns.
70
+ #
71
+ # Closing stdin, stdout and stderr does not wait the process.
72
+ #
73
+ def popen3(*cmd, &block)
74
+ if Hash === cmd.last
75
+ opts = cmd.pop.dup
76
+ else
77
+ opts = {}
78
+ end
79
+
80
+ in_r, in_w = IO.pipe
81
+ opts[:in] = in_r
82
+ in_w.sync = true
83
+
84
+ out_r, out_w = IO.pipe
85
+ opts[:out] = out_w
86
+
87
+ err_r, err_w = IO.pipe
88
+ opts[:err] = err_w
89
+
90
+ popen_run(cmd, opts, [in_r, out_w, err_w], [in_w, out_r, err_r], &block)
91
+ end
92
+ module_function :popen3
93
+
94
+ # Open3.popen2 is similer to Open3.popen3 except it doesn't make a pipe for
95
+ # the standard error stream.
96
+ #
97
+ # Block form:
98
+ #
99
+ # Open3.popen2([env,] cmd... [, opts]) {|stdin, stdout, wait_thr|
100
+ # pid = wait_thr.pid # pid of the started process.
101
+ # ...
102
+ # exit_status = wait_thr.value # Process::Status object returned.
103
+ # }
104
+ #
32
105
  # Non-block form:
33
- #
34
- # require 'open3'
35
106
  #
36
- # [stdin, stdout, stderr] = Open3.popen3(cmd)
107
+ # stdin, stdout, wait_thr = Open3.popen2([env,] cmd... [, opts])
108
+ # ...
109
+ # stdin.close # stdin and stdout should be closed explicitly in this form.
110
+ # stdout.close
111
+ #
112
+ # See Process.spawn for the optional hash arguments _env_ and _opts_.
113
+ #
114
+ # Example:
115
+ #
116
+ # Open3.popen2("wc -c") {|i,o,t|
117
+ # i.print "answer to life the universe and everything"
118
+ # i.close
119
+ # p o.gets #=> "42\n"
120
+ # }
121
+ #
122
+ # Open3.popen2("bc -q") {|i,o,t|
123
+ # i.puts "obase=13"
124
+ # i.puts "6 * 9"
125
+ # p o.gets #=> "42\n"
126
+ # }
127
+ #
128
+ # Open3.popen2("dc") {|i,o,t|
129
+ # i.print "42P"
130
+ # i.close
131
+ # p o.read #=> "*"
132
+ # }
133
+ #
134
+ def popen2(*cmd, &block)
135
+ if Hash === cmd.last
136
+ opts = cmd.pop.dup
137
+ else
138
+ opts = {}
139
+ end
140
+
141
+ in_r, in_w = IO.pipe
142
+ opts[:in] = in_r
143
+ in_w.sync = true
144
+
145
+ out_r, out_w = IO.pipe
146
+ opts[:out] = out_w
147
+
148
+ popen_run(cmd, opts, [in_r, out_w], [in_w, out_r], &block)
149
+ end
150
+ module_function :popen2
151
+
152
+ # Open3.popen2e is similer to Open3.popen3 except it merges
153
+ # the standard output stream and the standard error stream.
37
154
  #
38
155
  # Block form:
39
156
  #
40
- # require 'open3'
157
+ # Open3.popen2e([env,] cmd... [, opts]) {|stdin, stdout_and_stderr, wait_thr|
158
+ # pid = wait_thr.pid # pid of the started process.
159
+ # ...
160
+ # exit_status = wait_thr.value # Process::Status object returned.
161
+ # }
162
+ #
163
+ # Non-block form:
164
+ #
165
+ # stdin, stdout_and_stderr, wait_thr = Open3.popen2e([env,] cmd... [, opts])
166
+ # ...
167
+ # stdin.close # stdin and stdout_and_stderr should be closed explicitly in this form.
168
+ # stdout_and_stderr.close
41
169
  #
42
- # Open3.popen3(cmd) { |stdin, stdout, stderr| ... }
170
+ # See Process.spawn for the optional hash arguments _env_ and _opts_.
43
171
  #
44
- # The parameter +cmd+ is passed directly to Kernel#exec.
172
+ # Example:
173
+ # # check gcc warnings
174
+ # source = "foo.c"
175
+ # Open3.popen2e("gcc", "-Wall", source) {|i,oe,t|
176
+ # oe.each {|line|
177
+ # if /warning/ =~ line
178
+ # ...
179
+ # end
180
+ # }
181
+ # }
45
182
  #
46
- def popen3(*cmd)
47
- pw = IO.pipe # pipe[0] for read, pipe[1] for write
48
- pr = IO.pipe
49
- pe = IO.pipe
183
+ def popen2e(*cmd, &block)
184
+ if Hash === cmd.last
185
+ opts = cmd.pop.dup
186
+ else
187
+ opts = {}
188
+ end
189
+
190
+ in_r, in_w = IO.pipe
191
+ opts[:in] = in_r
192
+ in_w.sync = true
193
+
194
+ out_r, out_w = IO.pipe
195
+ opts[[:out, :err]] = out_w
50
196
 
51
- pid = fork{
52
- # child
53
- fork {
54
- # grandchild
55
- pw[1].close
56
- STDIN.reopen(pw[0])
57
- pw[0].close
197
+ popen_run(cmd, opts, [in_r, out_w], [in_w, out_r], &block)
198
+ end
199
+ module_function :popen2e
200
+
201
+ def popen_run(cmd, opts, child_io, parent_io) # :nodoc:
202
+ pid = spawn(*cmd, opts)
203
+ wait_thr = Process.detach(pid)
204
+ child_io.each {|io| io.close }
205
+ result = [*parent_io, wait_thr]
206
+ if defined? yield
207
+ begin
208
+ return yield(*result)
209
+ ensure
210
+ parent_io.each{|io| io.close unless io.closed?}
211
+ wait_thr.join
212
+ end
213
+ end
214
+ result
215
+ end
216
+ module_function :popen_run
217
+ class << self
218
+ private :popen_run
219
+ end
58
220
 
59
- pr[0].close
60
- STDOUT.reopen(pr[1])
61
- pr[1].close
221
+ # Open3.capture3 captures the standard output and the standard error of a command.
222
+ #
223
+ # stdout_str, stderr_str, status = Open3.capture3([env,] cmd... [, opts])
224
+ #
225
+ # The arguments env, cmd and opts are passed to Open3.popen3 except
226
+ # opts[:stdin_data] and opts[:stdin_data]. See Process.spawn.
227
+ #
228
+ # If opts[:stdin_data] is specified, it is sent to the command's standard input.
229
+ #
230
+ # If opts[:binmode] is true, internal pipes are set to binary mode.
231
+ #
232
+ # Example:
233
+ #
234
+ # # dot is a command of graphviz.
235
+ # graph = <<'End'
236
+ # digraph g {
237
+ # a -> b
238
+ # }
239
+ # End
240
+ # layouted_graph, dot_log = Open3.capture3("dot -v", :stdin_data=>graph)
241
+ #
242
+ # o, e, s = Open3.capture3("echo a; sort >&2", :stdin_data=>"foo\nbar\nbaz\n")
243
+ # p o #=> "a\n"
244
+ # p e #=> "bar\nbaz\nfoo\n"
245
+ # p s #=> #<Process::Status: pid 32682 exit 0>
246
+ #
247
+ # # generate a thumnail image using the convert command of ImageMagick.
248
+ # # However, if the image stored really in a file,
249
+ # # system("convert", "-thumbnail", "80", "png:#{filename}", "png:-") is better
250
+ # # because memory consumption.
251
+ # # But if the image is stored in a DB or generated by gnuplot Open3.capture2 example,
252
+ # # Open3.capture3 is considerable.
253
+ # #
254
+ # image = File.read("/usr/share/openclipart/png/animals/mammals/sheep-md-v0.1.png", :binmode=>true)
255
+ # thumnail, err, s = Open3.capture3("convert -thumbnail 80 png:- png:-", :stdin_data=>image, :binmode=>true)
256
+ # if s.success?
257
+ # STDOUT.binmode; print thumnail
258
+ # end
259
+ #
260
+ def capture3(*cmd, &block)
261
+ if Hash === cmd.last
262
+ opts = cmd.pop.dup
263
+ else
264
+ opts = {}
265
+ end
62
266
 
63
- pe[0].close
64
- STDERR.reopen(pe[1])
65
- pe[1].close
267
+ stdin_data = opts.delete(:stdin_data) || ''
268
+ binmode = opts.delete(:binmode)
66
269
 
67
- exec(*cmd)
68
- }
69
- exit!(0)
270
+ popen3(*cmd, opts) {|i, o, e, t|
271
+ if binmode
272
+ i.binmode
273
+ o.binmode
274
+ e.binmode
275
+ end
276
+ out_reader = Thread.new { o.read }
277
+ err_reader = Thread.new { e.read }
278
+ i.write stdin_data
279
+ i.close
280
+ [out_reader.value, err_reader.value, t.value]
70
281
  }
282
+ end
283
+ module_function :capture3
284
+
285
+ # Open3.capture2 captures the standard output of a command.
286
+ #
287
+ # stdout_str, status = Open3.capture2([env,] cmd... [, opts])
288
+ #
289
+ # The arguments env, cmd and opts are passed to Open3.popen3 except
290
+ # opts[:stdin_data] and opts[:stdin_data]. See Process.spawn.
291
+ #
292
+ # If opts[:stdin_data] is specified, it is sent to the command's standard input.
293
+ #
294
+ # If opts[:binmode] is true, internal pipes are set to binary mode.
295
+ #
296
+ # Example:
297
+ #
298
+ # # factor is a command for integer factorization.
299
+ # o, s = Open3.capture2("factor", :stdin_data=>"42")
300
+ # p o #=> "42: 2 3 7\n"
301
+ #
302
+ # # generate x**2 graph in png using gnuplot.
303
+ # gnuplot_commands = <<"End"
304
+ # set terminal png
305
+ # plot x**2, "-" with lines
306
+ # 1 14
307
+ # 2 1
308
+ # 3 8
309
+ # 4 5
310
+ # e
311
+ # End
312
+ # image, s = Open3.capture2("gnuplot", :stdin_data=>gnuplot_commands, :binmode=>true)
313
+ #
314
+ def capture2(*cmd, &block)
315
+ if Hash === cmd.last
316
+ opts = cmd.pop.dup
317
+ else
318
+ opts = {}
319
+ end
71
320
 
72
- pw[0].close
73
- pr[1].close
74
- pe[1].close
321
+ stdin_data = opts.delete(:stdin_data) || ''
322
+ binmode = opts.delete(:binmode)
75
323
 
76
- Process.waitpid(pid)
324
+ popen2(*cmd, opts) {|i, o, t|
325
+ if binmode
326
+ i.binmode
327
+ o.binmode
328
+ end
329
+ out_reader = Thread.new { o.read }
330
+ i.write stdin_data
331
+ i.close
332
+ [out_reader.value, t.value]
333
+ }
334
+ end
335
+ module_function :capture2
77
336
 
78
- pi = [pw[1], pr[0], pe[0]]
79
- pw[1].sync = true
337
+ # Open3.capture2e captures the standard output and the standard error of a command.
338
+ #
339
+ # stdout_and_stderr_str, status = Open3.capture2e([env,] cmd... [, opts])
340
+ #
341
+ # The arguments env, cmd and opts are passed to Open3.popen3 except
342
+ # opts[:stdin_data] and opts[:stdin_data]. See Process.spawn.
343
+ #
344
+ # If opts[:stdin_data] is specified, it is sent to the command's standard input.
345
+ #
346
+ # If opts[:binmode] is true, internal pipes are set to binary mode.
347
+ #
348
+ # Example:
349
+ #
350
+ # # capture make log
351
+ # make_log, s = Open3.capture2e("make")
352
+ #
353
+ def capture2e(*cmd, &block)
354
+ if Hash === cmd.last
355
+ opts = cmd.pop.dup
356
+ else
357
+ opts = {}
358
+ end
80
359
 
81
- if block_given?
360
+ stdin_data = opts.delete(:stdin_data) || ''
361
+ binmode = opts.delete(:binmode)
362
+
363
+ popen2e(*cmd, opts) {|i, oe, t|
364
+ if binmode
365
+ i.binmode
366
+ oe.binmode
367
+ end
368
+ outerr_reader = Thread.new { oe.read }
369
+ i.write stdin_data
370
+ i.close
371
+ [outerr_reader.value, t.value]
372
+ }
373
+ end
374
+ module_function :capture2e
375
+
376
+ # Open3.pipeline_rw starts a list of commands as a pipeline with pipes
377
+ # which connects stdin of the first command and stdout of the last command.
378
+ #
379
+ # Open3.pipeline_rw(cmd1, cmd2, ... [, opts]) {|first_stdin, last_stdout, wait_threads|
380
+ # ...
381
+ # }
382
+ #
383
+ # first_stdin, last_stdout, wait_threads = Open3.pipeline_rw(cmd1, cmd2, ... [, opts])
384
+ # ...
385
+ # first_stdin.close
386
+ # last_stdout.close
387
+ #
388
+ # Each cmd is a string or an array.
389
+ # If it is an array, the elements are passed to Process.spawn.
390
+ #
391
+ # cmd:
392
+ # commandline command line string which is passed to a shell
393
+ # [env, commandline, opts] command line string which is passed to a shell
394
+ # [env, cmdname, arg1, ..., opts] command name and one or more arguments (no shell)
395
+ # [env, [cmdname, argv0], arg1, ..., opts] command name and arguments including argv[0] (no shell)
396
+ #
397
+ # Note that env and opts are optional, as Process.spawn.
398
+ #
399
+ # The option to pass Process.spawn is constructed by merging
400
+ # +opts+, the last hash element of the array and
401
+ # specification for the pipe between each commands.
402
+ #
403
+ # Example:
404
+ #
405
+ # Open3.pipeline_rw("tr -dc A-Za-z", "wc -c") {|i,o,ts|
406
+ # i.puts "All persons more than a mile high to leave the court."
407
+ # i.close
408
+ # p o.gets #=> "42\n"
409
+ # }
410
+ #
411
+ # Open3.pipeline_rw("sort", "cat -n") {|stdin, stdout, wait_thrs|
412
+ # stdin.puts "foo"
413
+ # stdin.puts "bar"
414
+ # stdin.puts "baz"
415
+ # stdin.close # send EOF to sort.
416
+ # p stdout.read #=> " 1\tbar\n 2\tbaz\n 3\tfoo\n"
417
+ # }
418
+ def pipeline_rw(*cmds, &block)
419
+ if Hash === cmds.last
420
+ opts = cmds.pop.dup
421
+ else
422
+ opts = {}
423
+ end
424
+
425
+ in_r, in_w = IO.pipe
426
+ opts[:in] = in_r
427
+ in_w.sync = true
428
+
429
+ out_r, out_w = IO.pipe
430
+ opts[:out] = out_w
431
+
432
+ pipeline_run(cmds, opts, [in_r, out_w], [in_w, out_r], &block)
433
+ end
434
+ module_function :pipeline_rw
435
+
436
+ # Open3.pipeline_r starts a list of commands as a pipeline with a pipe
437
+ # which connects stdout of the last command.
438
+ #
439
+ # Open3.pipeline_r(cmd1, cmd2, ... [, opts]) {|last_stdout, wait_threads|
440
+ # ...
441
+ # }
442
+ #
443
+ # last_stdout, wait_threads = Open3.pipeline_r(cmd1, cmd2, ... [, opts])
444
+ # ...
445
+ # last_stdout.close
446
+ #
447
+ # Each cmd is a string or an array.
448
+ # If it is an array, the elements are passed to Process.spawn.
449
+ #
450
+ # cmd:
451
+ # commandline command line string which is passed to a shell
452
+ # [env, commandline, opts] command line string which is passed to a shell
453
+ # [env, cmdname, arg1, ..., opts] command name and one or more arguments (no shell)
454
+ # [env, [cmdname, argv0], arg1, ..., opts] command name and arguments including argv[0] (no shell)
455
+ #
456
+ # Note that env and opts are optional, as Process.spawn.
457
+ #
458
+ # Example:
459
+ #
460
+ # Open3.pipeline_r("zcat /var/log/apache2/access.log.*.gz",
461
+ # [{"LANG"=>"C"}, "grep", "GET /favicon.ico"],
462
+ # "logresolve") {|o, ts|
463
+ # o.each_line {|line|
464
+ # ...
465
+ # }
466
+ # }
467
+ #
468
+ # Open3.pipeline_r("yes", "head -10") {|o, ts|
469
+ # p o.read #=> "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"
470
+ # p ts[0].value #=> #<Process::Status: pid 24910 SIGPIPE (signal 13)>
471
+ # p ts[1].value #=> #<Process::Status: pid 24913 exit 0>
472
+ # }
473
+ #
474
+ def pipeline_r(*cmds, &block)
475
+ if Hash === cmds.last
476
+ opts = cmds.pop.dup
477
+ else
478
+ opts = {}
479
+ end
480
+
481
+ out_r, out_w = IO.pipe
482
+ opts[:out] = out_w
483
+
484
+ pipeline_run(cmds, opts, [out_w], [out_r], &block)
485
+ end
486
+ module_function :pipeline_r
487
+
488
+ # Open3.pipeline_w starts a list of commands as a pipeline with a pipe
489
+ # which connects stdin of the first command.
490
+ #
491
+ # Open3.pipeline_w(cmd1, cmd2, ... [, opts]) {|first_stdin, wait_threads|
492
+ # ...
493
+ # }
494
+ #
495
+ # first_stdin, wait_threads = Open3.pipeline_w(cmd1, cmd2, ... [, opts])
496
+ # ...
497
+ # first_stdin.close
498
+ #
499
+ # Each cmd is a string or an array.
500
+ # If it is an array, the elements are passed to Process.spawn.
501
+ #
502
+ # cmd:
503
+ # commandline command line string which is passed to a shell
504
+ # [env, commandline, opts] command line string which is passed to a shell
505
+ # [env, cmdname, arg1, ..., opts] command name and one or more arguments (no shell)
506
+ # [env, [cmdname, argv0], arg1, ..., opts] command name and arguments including argv[0] (no shell)
507
+ #
508
+ # Note that env and opts are optional, as Process.spawn.
509
+ #
510
+ # Example:
511
+ #
512
+ # Open3.pipeline_w("bzip2 -c", :out=>"/tmp/hello.bz2") {|i, ts|
513
+ # i.puts "hello"
514
+ # }
515
+ #
516
+ def pipeline_w(*cmds, &block)
517
+ if Hash === cmds.last
518
+ opts = cmds.pop.dup
519
+ else
520
+ opts = {}
521
+ end
522
+
523
+ in_r, in_w = IO.pipe
524
+ opts[:in] = in_r
525
+ in_w.sync = true
526
+
527
+ pipeline_run(cmds, opts, [in_r], [in_w], &block)
528
+ end
529
+ module_function :pipeline_w
530
+
531
+ # Open3.pipeline_start starts a list of commands as a pipeline.
532
+ # No pipe made for stdin of the first command and
533
+ # stdout of the last command.
534
+ #
535
+ # Open3.pipeline_start(cmd1, cmd2, ... [, opts]) {|wait_threads|
536
+ # ...
537
+ # }
538
+ #
539
+ # wait_threads = Open3.pipeline_start(cmd1, cmd2, ... [, opts])
540
+ # ...
541
+ #
542
+ # Each cmd is a string or an array.
543
+ # If it is an array, the elements are passed to Process.spawn.
544
+ #
545
+ # cmd:
546
+ # commandline command line string which is passed to a shell
547
+ # [env, commandline, opts] command line string which is passed to a shell
548
+ # [env, cmdname, arg1, ..., opts] command name and one or more arguments (no shell)
549
+ # [env, [cmdname, argv0], arg1, ..., opts] command name and arguments including argv[0] (no shell)
550
+ #
551
+ # Note that env and opts are optional, as Process.spawn.
552
+ #
553
+ # Example:
554
+ #
555
+ # # run xeyes in 10 seconds.
556
+ # Open3.pipeline_start("xeyes") {|ts|
557
+ # sleep 10
558
+ # t = ts[0]
559
+ # Process.kill("TERM", t.pid)
560
+ # p t.value #=> #<Process::Status: pid 911 SIGTERM (signal 15)>
561
+ # }
562
+ #
563
+ # # convert pdf to ps and send it to a printer.
564
+ # # collect error message of pdftops and lpr.
565
+ # pdf_file = "paper.pdf"
566
+ # printer = "printer-name"
567
+ # err_r, err_w = IO.pipe
568
+ # Open3.pipeline_start(["pdftops", pdf_file, "-"],
569
+ # ["lpr", "-P#{printer}"],
570
+ # :err=>err_w) {|ts|
571
+ # err_w.close
572
+ # p err_r.read # error messages of pdftops and lpr.
573
+ # }
574
+ #
575
+ def pipeline_start(*cmds, &block)
576
+ if Hash === cmds.last
577
+ opts = cmds.pop.dup
578
+ else
579
+ opts = {}
580
+ end
581
+
582
+ if block
583
+ pipeline_run(cmds, opts, [], [], &block)
584
+ else
585
+ ts, = pipeline_run(cmds, opts, [], [])
586
+ ts
587
+ end
588
+ end
589
+ module_function :pipeline_start
590
+
591
+ # Open3.pipeline starts a list of commands as a pipeline.
592
+ # It waits the finish of the commands.
593
+ # No pipe made for stdin of the first command and
594
+ # stdout of the last command.
595
+ #
596
+ # status_list = Open3.pipeline(cmd1, cmd2, ... [, opts])
597
+ #
598
+ # Each cmd is a string or an array.
599
+ # If it is an array, the elements are passed to Process.spawn.
600
+ #
601
+ # cmd:
602
+ # commandline command line string which is passed to a shell
603
+ # [env, commandline, opts] command line string which is passed to a shell
604
+ # [env, cmdname, arg1, ..., opts] command name and one or more arguments (no shell)
605
+ # [env, [cmdname, argv0], arg1, ..., opts] command name and arguments including argv[0] (no shell)
606
+ #
607
+ # Note that env and opts are optional, as Process.spawn.
608
+ #
609
+ # Example:
610
+ #
611
+ # fname = "/usr/share/man/man1/ruby.1.gz"
612
+ # p Open3.pipeline(["zcat", fname], "nroff -man", "less")
613
+ # #=> [#<Process::Status: pid 11817 exit 0>,
614
+ # # #<Process::Status: pid 11820 exit 0>,
615
+ # # #<Process::Status: pid 11828 exit 0>]
616
+ #
617
+ # fname = "/usr/share/man/man1/ls.1.gz"
618
+ # Open3.pipeline(["zcat", fname], "nroff -man", "colcrt")
619
+ #
620
+ # # convert PDF to PS and send to a printer by lpr
621
+ # pdf_file = "paper.pdf"
622
+ # printer = "printer-name"
623
+ # Open3.pipeline(["pdftops", pdf_file, "-"],
624
+ # ["lpr", "-P#{printer}"])
625
+ #
626
+ # # count lines
627
+ # Open3.pipeline("sort", "uniq -c", :in=>"names.txt", :out=>"count")
628
+ #
629
+ # # cyclic pipeline
630
+ # r,w = IO.pipe
631
+ # w.print "ibase=14\n10\n"
632
+ # Open3.pipeline("bc", "tee /dev/tty", :in=>r, :out=>w)
633
+ # #=> 14
634
+ # # 18
635
+ # # 22
636
+ # # 30
637
+ # # 42
638
+ # # 58
639
+ # # 78
640
+ # # 106
641
+ # # 202
642
+ #
643
+ def pipeline(*cmds)
644
+ if Hash === cmds.last
645
+ opts = cmds.pop.dup
646
+ else
647
+ opts = {}
648
+ end
649
+
650
+ pipeline_run(cmds, opts, [], []) {|ts|
651
+ ts.map {|t| t.value }
652
+ }
653
+ end
654
+ module_function :pipeline
655
+
656
+ def pipeline_run(cmds, pipeline_opts, child_io, parent_io, &block) # :nodoc:
657
+ if cmds.empty?
658
+ raise ArgumentError, "no commands"
659
+ end
660
+
661
+ opts_base = pipeline_opts.dup
662
+ opts_base.delete :in
663
+ opts_base.delete :out
664
+
665
+ wait_thrs = []
666
+ r = nil
667
+ cmds.each_with_index {|cmd, i|
668
+ cmd_opts = opts_base.dup
669
+ if String === cmd
670
+ cmd = [cmd]
671
+ else
672
+ cmd_opts.update cmd.pop if Hash === cmd.last
673
+ end
674
+ if i == 0
675
+ if !cmd_opts.include?(:in)
676
+ if pipeline_opts.include?(:in)
677
+ cmd_opts[:in] = pipeline_opts[:in]
678
+ end
679
+ end
680
+ else
681
+ cmd_opts[:in] = r
682
+ end
683
+ if i != cmds.length - 1
684
+ r2, w2 = IO.pipe
685
+ cmd_opts[:out] = w2
686
+ else
687
+ if !cmd_opts.include?(:out)
688
+ if pipeline_opts.include?(:out)
689
+ cmd_opts[:out] = pipeline_opts[:out]
690
+ end
691
+ end
692
+ end
693
+ pid = spawn(*cmd, cmd_opts)
694
+ wait_thrs << Process.detach(pid)
695
+ r.close if r
696
+ w2.close if w2
697
+ r = r2
698
+ }
699
+ result = parent_io + [wait_thrs]
700
+ child_io.each {|io| io.close }
701
+ if defined? yield
82
702
  begin
83
- return yield(*pi)
703
+ return yield(*result)
84
704
  ensure
85
- pi.each { |p| p.close unless p.closed? }
705
+ parent_io.each{|io| io.close unless io.closed?}
706
+ wait_thrs.each {|t| t.join }
86
707
  end
87
708
  end
88
- pi
709
+ result
89
710
  end
90
- module_function :popen3
711
+ module_function :pipeline_run
712
+ class << self
713
+ private :pipeline_run
714
+ end
715
+
91
716
  end
92
717
 
93
718
  if $0 == __FILE__
@@ -1,5 +1,5 @@
1
1
  module RubySL
2
2
  module Open3
3
- VERSION = "1.0.0"
3
+ VERSION = "2.0.0"
4
4
  end
5
5
  end
@@ -19,5 +19,4 @@ Gem::Specification.new do |spec|
19
19
  spec.add_development_dependency "bundler", "~> 1.3"
20
20
  spec.add_development_dependency "rake", "~> 10.0"
21
21
  spec.add_development_dependency "mspec", "~> 1.5"
22
- spec.add_development_dependency "rubysl-prettyprint", "~> 1.0"
23
- end
22
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubysl-open3
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Shirai
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-26 00:00:00.000000000 Z
11
+ date: 2013-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,20 +52,6 @@ dependencies:
52
52
  - - ~>
53
53
  - !ruby/object:Gem::Version
54
54
  version: '1.5'
55
- - !ruby/object:Gem::Dependency
56
- name: rubysl-prettyprint
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ~>
60
- - !ruby/object:Gem::Version
61
- version: '1.0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ~>
67
- - !ruby/object:Gem::Version
68
- version: '1.0'
69
55
  description: Ruby standard library open3.
70
56
  email:
71
57
  - brixen@gmail.com