coque 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +3 -3
- data/README.md +37 -0
- data/coque.gemspec +1 -1
- data/lib/coque/cmd.rb +6 -1
- data/lib/coque/pipeline.rb +7 -2
- data/lib/coque/rb.rb +1 -1
- data/lib/coque/redirectable.rb +15 -17
- data/lib/coque/runnable.rb +38 -0
- data/lib/coque/sh.rb +1 -1
- data/lib/coque/version.rb +1 -1
- data/lib/coque.rb +10 -0
- metadata +6 -7
- data/drake.rb +0 -113
- data/io.rb +0 -215
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f24fa9416a64dc742a217e43ef53cbb6d6fc3b0
|
4
|
+
data.tar.gz: 0bc4970fa99aeac4190fd30c4752a40a890ba96e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e0029d5784e6afc64595e38461fbb44c7003d0782fe9bc78864c57289dda090a1e943dac0d134e26870f041274aad223563916bb0825cc3a8dfcee20e1c38fa6
|
7
|
+
data.tar.gz: 2c6f2e2d665b75341081ac6d1cf7a924a41dcab2088da3d3c2d95096c8bfcda37a44bc3be4388fe588cd341a87f062a13147de506e8e1ff67900faea18d9a7fe
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
coque (0.
|
4
|
+
coque (0.6.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -33,7 +33,7 @@ PLATFORMS
|
|
33
33
|
ruby
|
34
34
|
|
35
35
|
DEPENDENCIES
|
36
|
-
bundler (~>
|
36
|
+
bundler (~> 2.0)
|
37
37
|
coque!
|
38
38
|
minitest (~> 5.11)
|
39
39
|
minitest-reporters
|
@@ -42,4 +42,4 @@ DEPENDENCIES
|
|
42
42
|
simplecov
|
43
43
|
|
44
44
|
BUNDLED WITH
|
45
|
-
|
45
|
+
2.0.1
|
data/README.md
CHANGED
@@ -99,6 +99,43 @@ Coque also includes a `Coque.source` helper for feeding Ruby enumerables into sh
|
|
99
99
|
# => ["500"]
|
100
100
|
```
|
101
101
|
|
102
|
+
#### Logging
|
103
|
+
|
104
|
+
You can set a logger for Coque, which will be used to output messages when commands are executed:
|
105
|
+
|
106
|
+
```rb
|
107
|
+
Coque.logger = Logger.new(STDOUT)
|
108
|
+
(Coque["echo", "hi"] | Coque["wc", "-c"]).run!
|
109
|
+
```
|
110
|
+
|
111
|
+
#### Named (Non-Operator) Method Alternatives
|
112
|
+
|
113
|
+
The main piping and redirection methods also include named alternatives:
|
114
|
+
|
115
|
+
* `|` is aliased to `pipe`
|
116
|
+
* `>` is aliased to `out`
|
117
|
+
* `>=` is aliased to `err`
|
118
|
+
* `<` is aliased to `in`
|
119
|
+
|
120
|
+
So these 2 invocations are equivalent:
|
121
|
+
|
122
|
+
```rb
|
123
|
+
(Coque["echo", "hi"] | Coque["wc", "-c"] > STDERR).run!
|
124
|
+
# is the same as...
|
125
|
+
Coque["echo", "hi"].pipe(Coque["wc", "-c"]).out(STDERR).run!
|
126
|
+
```
|
127
|
+
|
128
|
+
Will log:
|
129
|
+
|
130
|
+
```
|
131
|
+
I, [2019-02-20T20:31:00.325777 #16749] INFO -- : Executing Coque Command: <Pipeline <Coque::Sh echo hi> | <Coque::Sh wc -c> >
|
132
|
+
I, [2019-02-20T20:31:00.325971 #16749] INFO -- : Executing Coque Command: <Coque::Sh echo hi>
|
133
|
+
I, [2019-02-20T20:31:00.327719 #16749] INFO -- : Coque Command: <Coque::Sh echo hi> finished in 0.001683 seconds.
|
134
|
+
I, [2019-02-20T20:31:00.327771 #16749] INFO -- : Executing Coque Command: <Coque::Sh wc -c>
|
135
|
+
I, [2019-02-20T20:31:00.329586 #16749] INFO -- : Coque Command: <Coque::Sh wc -c> finished in 0.001739 seconds.
|
136
|
+
I, [2019-02-20T20:31:00.329725 #16749] INFO -- : Coque Command: <Pipeline <Coque::Sh echo hi> | <Coque::Sh wc -c> > finished in 0.003796 seconds.
|
137
|
+
```
|
138
|
+
|
102
139
|
### Streaming Performance
|
103
140
|
|
104
141
|
Should be little overhead compared with the equivalent pipeline from a standard shell.
|
data/coque.gemspec
CHANGED
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
31
31
|
spec.require_paths = ["lib"]
|
32
32
|
|
33
|
-
spec.add_development_dependency "bundler", "~>
|
33
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
34
34
|
spec.add_development_dependency "rake", "~> 12.3"
|
35
35
|
spec.add_development_dependency "minitest", "~> 5.11"
|
36
36
|
spec.add_development_dependency "minitest-reporters"
|
data/lib/coque/cmd.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
module Coque
|
2
2
|
class Cmd
|
3
3
|
include Redirectable
|
4
|
+
include Runnable
|
4
5
|
attr_reader :context
|
5
6
|
|
6
|
-
def
|
7
|
+
def pipe(other)
|
7
8
|
case other
|
8
9
|
when Cmd
|
9
10
|
Pipeline.new([self.clone, other.clone])
|
@@ -12,6 +13,10 @@ module Coque
|
|
12
13
|
end
|
13
14
|
end
|
14
15
|
|
16
|
+
def |(other)
|
17
|
+
pipe(other)
|
18
|
+
end
|
19
|
+
|
15
20
|
def clone
|
16
21
|
raise "Not Implemented - Override"
|
17
22
|
end
|
data/lib/coque/pipeline.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module Coque
|
2
2
|
class Pipeline
|
3
3
|
include Redirectable
|
4
|
+
include Runnable
|
4
5
|
|
5
6
|
attr_reader :commands
|
6
7
|
def initialize(commands = [])
|
@@ -15,7 +16,7 @@ module Coque
|
|
15
16
|
"<Pipeline #{commands.join(" | ")} >"
|
16
17
|
end
|
17
18
|
|
18
|
-
def
|
19
|
+
def pipe(other)
|
19
20
|
case other
|
20
21
|
when Pipeline
|
21
22
|
Pipeline.new(commands + other.commands)
|
@@ -24,6 +25,10 @@ module Coque
|
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
28
|
+
def |(other)
|
29
|
+
pipe(other)
|
30
|
+
end
|
31
|
+
|
27
32
|
def stitch
|
28
33
|
# Set head in
|
29
34
|
if commands.first.stdin.nil?
|
@@ -52,7 +57,7 @@ module Coque
|
|
52
57
|
end
|
53
58
|
end
|
54
59
|
|
55
|
-
def
|
60
|
+
def get_result
|
56
61
|
stdout = stitch
|
57
62
|
results = commands.map(&:run)
|
58
63
|
Result.new(results.last.pid, stdout)
|
data/lib/coque/rb.rb
CHANGED
data/lib/coque/redirectable.rb
CHANGED
@@ -4,24 +4,36 @@ module Coque
|
|
4
4
|
module Redirectable
|
5
5
|
attr_reader :stdin, :stdout, :stderr
|
6
6
|
|
7
|
-
def
|
7
|
+
def out(io)
|
8
8
|
clone.tap do |c|
|
9
9
|
c.stdout = io
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
def
|
13
|
+
def >(io)
|
14
|
+
out(io)
|
15
|
+
end
|
16
|
+
|
17
|
+
def in(io)
|
14
18
|
clone.tap do |c|
|
15
19
|
c.stdin = io
|
16
20
|
end
|
17
21
|
end
|
18
22
|
|
19
|
-
def
|
23
|
+
def <(io)
|
24
|
+
self.in(io)
|
25
|
+
end
|
26
|
+
|
27
|
+
def err(io)
|
20
28
|
clone.tap do |c|
|
21
29
|
c.stderr = io
|
22
30
|
end
|
23
31
|
end
|
24
32
|
|
33
|
+
def >=(io)
|
34
|
+
err(io)
|
35
|
+
end
|
36
|
+
|
25
37
|
def getio(io, mode = "r")
|
26
38
|
case io
|
27
39
|
when STDERR
|
@@ -53,20 +65,6 @@ module Coque
|
|
53
65
|
@stdin = getio(s, "r")
|
54
66
|
end
|
55
67
|
|
56
|
-
def to_a
|
57
|
-
run.to_a
|
58
|
-
end
|
59
|
-
|
60
|
-
def success?
|
61
|
-
run.success?
|
62
|
-
end
|
63
|
-
|
64
|
-
def run!
|
65
|
-
if !success?
|
66
|
-
raise "Coque Command Failed: #{self}"
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
68
|
private
|
71
69
|
|
72
70
|
def stdout_read
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Coque
|
2
|
+
module Runnable
|
3
|
+
def to_a
|
4
|
+
run.to_a
|
5
|
+
end
|
6
|
+
|
7
|
+
def success?
|
8
|
+
run.success?
|
9
|
+
end
|
10
|
+
|
11
|
+
def run!
|
12
|
+
if !success?
|
13
|
+
raise "Coque Command Failed: #{self}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def run
|
18
|
+
log_start
|
19
|
+
start_time = Time.now
|
20
|
+
result = get_result
|
21
|
+
end_time = Time.now
|
22
|
+
log_end(end_time - start_time)
|
23
|
+
result
|
24
|
+
end
|
25
|
+
|
26
|
+
def log_end(seconds)
|
27
|
+
if Coque.logger
|
28
|
+
Coque.logger.info("Coque Command: #{self.to_s} finished in #{seconds} seconds.")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def log_start
|
33
|
+
if Coque.logger
|
34
|
+
Coque.logger.info("Executing Coque Command: #{self.to_s}")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/coque/sh.rb
CHANGED
data/lib/coque/version.rb
CHANGED
data/lib/coque.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "coque/redirectable"
|
2
|
+
require "coque/runnable"
|
2
3
|
require "coque/cmd"
|
3
4
|
require "coque/sh"
|
4
5
|
require "coque/rb"
|
@@ -9,6 +10,15 @@ require "coque/result"
|
|
9
10
|
require "coque/version"
|
10
11
|
|
11
12
|
module Coque
|
13
|
+
@@logger = nil
|
14
|
+
def self.logger=(logger)
|
15
|
+
@@logger = logger
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.logger
|
19
|
+
@@logger
|
20
|
+
end
|
21
|
+
|
12
22
|
def self.context(dir: Dir.pwd, env: {}, disinherits_env: false)
|
13
23
|
Context.new(dir, env, disinherits_env)
|
14
24
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: coque
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Horace Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-02-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '2.0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '2.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -112,8 +112,6 @@ files:
|
|
112
112
|
- bin/console
|
113
113
|
- bin/setup
|
114
114
|
- coque.gemspec
|
115
|
-
- drake.rb
|
116
|
-
- io.rb
|
117
115
|
- lib/coque.rb
|
118
116
|
- lib/coque/cmd.rb
|
119
117
|
- lib/coque/context.rb
|
@@ -122,6 +120,7 @@ files:
|
|
122
120
|
- lib/coque/rb.rb
|
123
121
|
- lib/coque/redirectable.rb
|
124
122
|
- lib/coque/result.rb
|
123
|
+
- lib/coque/runnable.rb
|
125
124
|
- lib/coque/sh.rb
|
126
125
|
- lib/coque/version.rb
|
127
126
|
- script.py
|
@@ -146,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
146
145
|
version: '0'
|
147
146
|
requirements: []
|
148
147
|
rubyforge_project:
|
149
|
-
rubygems_version: 2.
|
148
|
+
rubygems_version: 2.5.1
|
150
149
|
signing_key:
|
151
150
|
specification_version: 4
|
152
151
|
summary: Shell command utilities with easy integration with Ruby code.
|
data/drake.rb
DELETED
@@ -1,113 +0,0 @@
|
|
1
|
-
require "open3"
|
2
|
-
require "pry"
|
3
|
-
|
4
|
-
class Pipeline
|
5
|
-
attr_reader :commands
|
6
|
-
def initialize(commands = [])
|
7
|
-
@commands = commands
|
8
|
-
end
|
9
|
-
|
10
|
-
def to_s
|
11
|
-
"Pipeline of #{commands.join("|")}"
|
12
|
-
end
|
13
|
-
|
14
|
-
def |(other)
|
15
|
-
case other
|
16
|
-
when Pipeline
|
17
|
-
Pipeline.new(commands + other.commands)
|
18
|
-
when Cmd
|
19
|
-
Pipeline.new(commands + [other])
|
20
|
-
when Crb
|
21
|
-
Pipeline.new(commands + [other])
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def run
|
26
|
-
commands = self.commands
|
27
|
-
pids = []
|
28
|
-
|
29
|
-
in_read, in_write = IO.pipe
|
30
|
-
out_read = nil
|
31
|
-
out_write = nil
|
32
|
-
|
33
|
-
while commands.any? do
|
34
|
-
in_write.close
|
35
|
-
out_read, out_write = IO.pipe
|
36
|
-
|
37
|
-
cmd = commands.shift
|
38
|
-
puts "Spawn command: #{cmd}"
|
39
|
-
pids << cmd.run(in_read, out_write)
|
40
|
-
|
41
|
-
in_read = out_read
|
42
|
-
in_write = out_write
|
43
|
-
end
|
44
|
-
out_write.close
|
45
|
-
[pids, out_read]
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
class Cmd
|
50
|
-
attr_reader :args
|
51
|
-
def initialize(args)
|
52
|
-
@args = args
|
53
|
-
end
|
54
|
-
|
55
|
-
def to_s
|
56
|
-
"Cmd to run: `#{args.inspect}`"
|
57
|
-
end
|
58
|
-
|
59
|
-
def self.[](*args)
|
60
|
-
puts args.inspect
|
61
|
-
Cmd.new(args)
|
62
|
-
end
|
63
|
-
|
64
|
-
def command
|
65
|
-
args.join(" ")
|
66
|
-
end
|
67
|
-
|
68
|
-
def run(stdin, stdout)
|
69
|
-
spawn(args.join(" "), in: stdin, stdin => stdin, out: stdout, stdout => stdout)
|
70
|
-
end
|
71
|
-
|
72
|
-
def |(other)
|
73
|
-
case other
|
74
|
-
when Cmd
|
75
|
-
Pipeline.new([self, other])
|
76
|
-
when Crb
|
77
|
-
Pipeline.new([self, other])
|
78
|
-
when Pipeline
|
79
|
-
Pipeline.new([self] + other.commands)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
class Crb
|
85
|
-
def initialize(&block)
|
86
|
-
@block = block
|
87
|
-
end
|
88
|
-
|
89
|
-
def run(stdin, stdout)
|
90
|
-
fork do
|
91
|
-
STDOUT.reopen(stdout)
|
92
|
-
stdin.each_line(&@block)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
def |(other)
|
97
|
-
case other
|
98
|
-
when Cmd
|
99
|
-
Pipeline.new([self, other])
|
100
|
-
when Crb
|
101
|
-
Pipeline.new([self, other])
|
102
|
-
when Pipeline
|
103
|
-
Pipeline.new([self] + other.commands)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
|
109
|
-
c = Cmd['cat', '/usr/share/dict/words'] | Cmd['head'] | Crb.new { |line| puts "crb - #{line}" }
|
110
|
-
puts "pipeline: #{c}"
|
111
|
-
pids, out = c.run
|
112
|
-
puts "Spawned pids: #{pids}"
|
113
|
-
out.each_line { |l| puts l }
|
data/io.rb
DELETED
@@ -1,215 +0,0 @@
|
|
1
|
-
require 'open3'
|
2
|
-
|
3
|
-
def banner(msg)
|
4
|
-
puts "******* #{msg} *******"
|
5
|
-
end
|
6
|
-
|
7
|
-
def read_from_spawned_pipe
|
8
|
-
banner("read_from_spawned_pipe")
|
9
|
-
pipe_me_in, pipe_peer_out = IO.pipe
|
10
|
-
pipe_peer_in, pipe_me_out = IO.pipe
|
11
|
-
|
12
|
-
pid = spawn('ls',
|
13
|
-
out: pipe_peer_out,
|
14
|
-
pipe_peer_out => pipe_peer_out,
|
15
|
-
in: pipe_peer_in,
|
16
|
-
pipe_peer_in => pipe_peer_in)
|
17
|
-
puts "Spawned #{pid}"
|
18
|
-
|
19
|
-
pipe_peer_out.close
|
20
|
-
puts pipe_me_in.read
|
21
|
-
end
|
22
|
-
|
23
|
-
def read_write_to_spawned
|
24
|
-
banner("read_write_to_spawned")
|
25
|
-
pipe_me_in, pipe_peer_out = IO.pipe
|
26
|
-
pipe_peer_in, pipe_me_out = IO.pipe
|
27
|
-
|
28
|
-
['a', 'b', 'c', 'ab'].each { |l| pipe_me_out.puts(l) }
|
29
|
-
|
30
|
-
pid = spawn('grep a',
|
31
|
-
out: pipe_peer_out,
|
32
|
-
pipe_peer_out => pipe_peer_out,
|
33
|
-
in: pipe_peer_in,
|
34
|
-
pipe_peer_in => pipe_peer_in)
|
35
|
-
puts "Spawned #{pid}"
|
36
|
-
pipe_me_out.close
|
37
|
-
|
38
|
-
pipe_peer_out.close
|
39
|
-
puts pipe_me_in.read
|
40
|
-
end
|
41
|
-
|
42
|
-
def chain_two_native_processes
|
43
|
-
banner("chain_two_native_processes")
|
44
|
-
a_in_read, a_in_write = IO.pipe
|
45
|
-
a_out_read, a_out_write = IO.pipe
|
46
|
-
|
47
|
-
p1 = spawn('echo "a\nb\nc\nab\n"',
|
48
|
-
out: a_out_write,
|
49
|
-
a_out_write => a_out_write,
|
50
|
-
in: a_in_read,
|
51
|
-
a_in_read => a_in_read)
|
52
|
-
|
53
|
-
b_out_read, b_out_write = IO.pipe
|
54
|
-
|
55
|
-
p2 = spawn('grep a',
|
56
|
-
out: b_out_write,
|
57
|
-
b_out_write => b_out_write,
|
58
|
-
in: a_out_read,
|
59
|
-
a_out_read => a_out_read)
|
60
|
-
|
61
|
-
puts "Spawned a: #{p1}, b: #{p2}"
|
62
|
-
# Q: Why do we have to close these? do the spawned processes not close them?
|
63
|
-
b_out_write.close
|
64
|
-
a_out_write.close
|
65
|
-
puts b_out_read.read
|
66
|
-
end
|
67
|
-
|
68
|
-
def reading_large_output
|
69
|
-
banner("reading_large_output")
|
70
|
-
a_in_read, a_in_write = IO.pipe
|
71
|
-
a_out_read, a_out_write = IO.pipe
|
72
|
-
|
73
|
-
p1 = spawn('cat /usr/share/dict/words',
|
74
|
-
out: a_out_write,
|
75
|
-
a_out_write => a_out_write)
|
76
|
-
|
77
|
-
a_out_write.close
|
78
|
-
puts a_out_read.read
|
79
|
-
end
|
80
|
-
|
81
|
-
def pipe_to_forked_ruby
|
82
|
-
banner("pipe_to_forked_ruby")
|
83
|
-
a_in_read, a_in_write = IO.pipe
|
84
|
-
b_out_read, b_out_write = IO.pipe
|
85
|
-
|
86
|
-
['a', 'b', 'c', 'ab'].each { |l| a_in_write.puts(l) }
|
87
|
-
a_in_write.flush
|
88
|
-
a_in_write.close
|
89
|
-
|
90
|
-
child = fork do
|
91
|
-
# change our stdin to be the read end of the pipe
|
92
|
-
STDOUT.puts "forked process to stdout (#{STDOUT.fileno})"
|
93
|
-
b_out_write.puts "******* forked process to pipe (#{b_out_write.fileno}) **********"
|
94
|
-
|
95
|
-
a_in_read.each_line { |l| b_out_write.puts "child - #{l}" }
|
96
|
-
end
|
97
|
-
puts "forked #{child}"
|
98
|
-
|
99
|
-
a_in_write.close
|
100
|
-
b_out_write.close
|
101
|
-
|
102
|
-
b_out_read.each_line { |l| puts "read from parent - #{l}" }
|
103
|
-
end
|
104
|
-
|
105
|
-
|
106
|
-
def native_process_to_ruby_block
|
107
|
-
banner("native_process_to_ruby_block")
|
108
|
-
# a_in_read, a_in_write = IO.pipe
|
109
|
-
a_out_read, a_out_write = IO.pipe
|
110
|
-
b_out_read, b_out_write = IO.pipe
|
111
|
-
puts "parent file descriptors"
|
112
|
-
puts "a read: #{a_out_read.fileno}"
|
113
|
-
puts "a write: #{a_out_write.fileno}"
|
114
|
-
puts "b read: #{b_out_read.fileno}"
|
115
|
-
puts "b write: #{b_out_write.fileno}"
|
116
|
-
|
117
|
-
# Receives copy of a_out_write; closes when done
|
118
|
-
p1 = spawn('echo "a\nb\nc\nab"', out: a_out_write)
|
119
|
-
|
120
|
-
# Receives copy of
|
121
|
-
# a_out_read - needs to read; closed automatically?
|
122
|
-
# a_out_write - doesn't need, close immediately
|
123
|
-
# b_out_read - doesn't need, close immediately
|
124
|
-
# b_out_write - needs to write; close when done
|
125
|
-
child = fork do
|
126
|
-
puts "child file descriptors:"
|
127
|
-
puts "a read: #{a_out_read.fileno}"
|
128
|
-
puts "a write: #{a_out_write.fileno}"
|
129
|
-
puts "b read: #{b_out_read.fileno}"
|
130
|
-
puts "b write: #{b_out_write.fileno}"
|
131
|
-
a_out_write.close
|
132
|
-
b_out_read.close
|
133
|
-
|
134
|
-
puts "******* IN FORK **********"
|
135
|
-
|
136
|
-
# while l = a_out_read.gets
|
137
|
-
# puts "working #{l}"
|
138
|
-
# b_out_write.puts "child - #{l}"
|
139
|
-
# end
|
140
|
-
|
141
|
-
a_out_read.each_line { |l| puts "working #{l}"; b_out_write.puts "child - #{l}" }
|
142
|
-
puts "child done - close writer"
|
143
|
-
a_out_read.close
|
144
|
-
b_out_write.close
|
145
|
-
puts "Fork work done"
|
146
|
-
end
|
147
|
-
|
148
|
-
puts "done forked"
|
149
|
-
|
150
|
-
a_out_write.close
|
151
|
-
b_out_write.close
|
152
|
-
a_out_read.close
|
153
|
-
|
154
|
-
puts "** Display child output:"
|
155
|
-
b_out_read.each_line { |l| puts "read from parent - #{l}" }
|
156
|
-
b_out_read.close
|
157
|
-
puts "done reading"
|
158
|
-
end
|
159
|
-
|
160
|
-
# read_from_spawned_pipe
|
161
|
-
# read_write_to_spawned
|
162
|
-
# pipe_to_forked_ruby
|
163
|
-
# reading_large_output
|
164
|
-
# chain_two_native_processes
|
165
|
-
# native_process_to_ruby_block
|
166
|
-
|
167
|
-
def run_fork(stdin, stdout, &block)
|
168
|
-
fork do
|
169
|
-
STDOUT.reopen(stdout)
|
170
|
-
stdin.each_line(&block)
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
def three_step
|
175
|
-
writers = []
|
176
|
-
banner("three step")
|
177
|
-
a_in_read, a_in_write = IO.pipe
|
178
|
-
a_out_read, a_out_write = IO.pipe
|
179
|
-
|
180
|
-
a_unused = [a_out_write]
|
181
|
-
|
182
|
-
p1 = spawn('echo "a\nb\nc\nab\n"',
|
183
|
-
out: a_out_write,
|
184
|
-
a_out_write => a_out_write,
|
185
|
-
in: a_in_read,
|
186
|
-
a_in_read => a_in_read)
|
187
|
-
|
188
|
-
b_out_read, b_out_write = IO.pipe
|
189
|
-
b_unused = [b_out_write]
|
190
|
-
|
191
|
-
a_unused.each(&:close)
|
192
|
-
run_fork(a_out_read, b_out_write) { |l| puts "~~ - #{l}" }
|
193
|
-
|
194
|
-
c_out_read, c_out_write = IO.pipe
|
195
|
-
c_unused = [c_out_write]
|
196
|
-
|
197
|
-
p2 = spawn('grep a',
|
198
|
-
out: c_out_write,
|
199
|
-
c_out_write => c_out_write,
|
200
|
-
in: b_out_read,
|
201
|
-
b_out_read => b_out_read)
|
202
|
-
b_unused.each(&:close)
|
203
|
-
|
204
|
-
# Q: Why do we have to close these? do the spawned processes not close them?
|
205
|
-
c_unused.each(&:close)
|
206
|
-
puts c_out_read.read
|
207
|
-
end
|
208
|
-
|
209
|
-
# three_step
|
210
|
-
|
211
|
-
def stdin_redirect
|
212
|
-
stdin = File.open("/usr/share/dict/words", "r")
|
213
|
-
spawn("head", in: stdin, stdin => stdin)
|
214
|
-
end
|
215
|
-
stdin_redirect
|