coque 0.6.0 → 0.7.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 +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
|