rubysl-open3 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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