rash-command-shell 0.2.2 → 0.4.2
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/bin/rash +1 -1
- data/lib/rash.rb +40 -3
- data/lib/rash/capturing.rb +45 -0
- data/lib/rash/ext/filesystem.rb +148 -23
- data/lib/rash/jobcontrol.rb +1 -1
- data/lib/rash/pipeline.rb +99 -9
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 00dbd88cd56e1de80255ce40b9a061048ae8ec96b70cb0dc5c36a78589e73c68
|
4
|
+
data.tar.gz: f31d65ea147f7c3fa5516d51b38e70c28fa3cadb482427393887f9d42ef34321
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e4194630eb9dbe5b1c4ad8afb9eb09e232b801eab51d06560cc1c41404d4202ade769c22a4da4f878f9d95ca213566635b90472e6c507e66578ddd2f465921f0
|
7
|
+
data.tar.gz: 5a12911de5b15ed865d14773b34b2a51f0efea3a3159bf29821eda79cf0ba7459429c7e4b1d95290cf03701745b4c914e4d9b8745906985e82a113f7be8351ab
|
data/bin/rash
CHANGED
@@ -4,7 +4,7 @@ if ARGV.empty?
|
|
4
4
|
exec("irb", "-r", "rash", *ARGV)
|
5
5
|
elsif ARGV[0] =~ /(-)?-v(ersion)?/
|
6
6
|
puts "Rash (c) 2020 Kellen Watt"
|
7
|
-
puts "Version 0.
|
7
|
+
puts "Version 0.4.2" # I may forget to update this
|
8
8
|
elsif File.exists?(ARGV[0]) && !File.directory?(ARGV[0])
|
9
9
|
require "rash"
|
10
10
|
file = ARGV.shift
|
data/lib/rash.rb
CHANGED
@@ -12,9 +12,24 @@ class Environment
|
|
12
12
|
Dir.chdir(dir.nil? ? "~" : dir.to_s)
|
13
13
|
@working_directory = Dir.pwd
|
14
14
|
ENV["OLDPWD"] = old.to_s
|
15
|
+
ENV["PWD"] = Dir.pwd
|
15
16
|
Dir.pwd
|
16
17
|
end
|
17
|
-
|
18
|
+
|
19
|
+
# Note that this works regardless of which version of chdir is used.
|
20
|
+
def push_dir(dir = nil)
|
21
|
+
@directory_stack.push(Dir.pwd)
|
22
|
+
self.chdir(dir)
|
23
|
+
end
|
24
|
+
|
25
|
+
def pop_dir
|
26
|
+
self.chdir(@directory_stack.pop) if @directory_stack.size > 0
|
27
|
+
end
|
28
|
+
|
29
|
+
def dirs
|
30
|
+
@directory_stack
|
31
|
+
end
|
32
|
+
|
18
33
|
def add_path(path)
|
19
34
|
ENV["PATH"] += File::PATH_SEPARATOR + (path.respond_to?(:path) ? path.path : path.to_s)
|
20
35
|
end
|
@@ -64,6 +79,10 @@ class Environment
|
|
64
79
|
end
|
65
80
|
end
|
66
81
|
|
82
|
+
def name?(v)
|
83
|
+
v.kind_of?(String) || v.kind_of?(Symbol)
|
84
|
+
end
|
85
|
+
|
67
86
|
private
|
68
87
|
|
69
88
|
def common_init
|
@@ -76,6 +95,8 @@ class Environment
|
|
76
95
|
|
77
96
|
@active_pipelines = []
|
78
97
|
|
98
|
+
@directory_stack = []
|
99
|
+
|
79
100
|
@prompt = {
|
80
101
|
AUTO_INDENT: true,
|
81
102
|
RETURN: ""
|
@@ -105,6 +126,7 @@ require_relative "rash/redirection"
|
|
105
126
|
require_relative "rash/aliasing"
|
106
127
|
require_relative "rash/jobcontrol"
|
107
128
|
require_relative "rash/pipeline"
|
129
|
+
require_relative "rash/capturing"
|
108
130
|
|
109
131
|
$env = Environment.new
|
110
132
|
|
@@ -120,6 +142,17 @@ def cd(dir = nil)
|
|
120
142
|
$env.chdir(d)
|
121
143
|
end
|
122
144
|
|
145
|
+
def pushd(dir = nil)
|
146
|
+
case dir
|
147
|
+
when File, Dir
|
148
|
+
dir = dir.path if File.directory(dir.path)
|
149
|
+
end
|
150
|
+
$env.push_dir(dir)
|
151
|
+
end
|
152
|
+
|
153
|
+
def popd
|
154
|
+
$env.pop_dir
|
155
|
+
end
|
123
156
|
|
124
157
|
def run(file, *args)
|
125
158
|
filename = file.to_s
|
@@ -128,7 +161,6 @@ def run(file, *args)
|
|
128
161
|
raise SystemCallError.new("No such executable file - #{exe}", Errno::ENOENT::Errno)
|
129
162
|
end
|
130
163
|
$env.dispatch(exe, *args, literal: true)
|
131
|
-
# system(exe, *args.flatten.map{|a| a.to_s}, {out: $stdout, err: $stderr, in: $stdin, umask: $env.umask})
|
132
164
|
end
|
133
165
|
|
134
166
|
alias cmd __send__
|
@@ -151,7 +183,7 @@ end
|
|
151
183
|
|
152
184
|
|
153
185
|
def which(command)
|
154
|
-
cmd = File.expand_path(command)
|
186
|
+
cmd = File.expand_path(command.to_s)
|
155
187
|
return cmd if File.executable?(cmd) && !File.directory?(cmd)
|
156
188
|
|
157
189
|
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
@@ -165,6 +197,11 @@ def which(command)
|
|
165
197
|
nil
|
166
198
|
end
|
167
199
|
|
200
|
+
# This breaks some default IRB functionality
|
201
|
+
# def self.respond_to_missing?(m, *args)
|
202
|
+
#
|
203
|
+
# which(m.to_s) || ($env.alias?(m) && !$env.aliasing_disabled) || $env.local_method?(m) || super
|
204
|
+
# end
|
168
205
|
|
169
206
|
# Note that I defy convention and don't define `respond_to_missing?`. This
|
170
207
|
# is because doing so screws with irb.
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class Environment
|
2
|
+
def capture_block(&block)
|
3
|
+
raise ArgumentError.new("no block provided") unless block_given?
|
4
|
+
result = nil
|
5
|
+
old_pipeline = @in_pipeline
|
6
|
+
begin
|
7
|
+
reader, writer = IO.pipe
|
8
|
+
self.stdout = writer
|
9
|
+
@in_pipeline = false
|
10
|
+
block.call
|
11
|
+
ensure
|
12
|
+
@in_pipeline = old_pipeline
|
13
|
+
reset_stdout
|
14
|
+
writer.close
|
15
|
+
result = reader.read
|
16
|
+
reader.close
|
17
|
+
end
|
18
|
+
result
|
19
|
+
end
|
20
|
+
|
21
|
+
def capture_command(m, *args)
|
22
|
+
raise NameError.new("no such command", m) unless which(m) || ($env.alias?(m) && !$env.aliasing_disabled)
|
23
|
+
result = nil
|
24
|
+
begin
|
25
|
+
reader, writer = IO.pipe
|
26
|
+
system_command(m, *args, out: writer)
|
27
|
+
ensure
|
28
|
+
writer.close
|
29
|
+
result = reader.read
|
30
|
+
reader.close
|
31
|
+
end
|
32
|
+
result
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# This explicitly doesn't support pipelining, as the output is ripped out of sequence.
|
37
|
+
def capture(*cmd, &block)
|
38
|
+
if block_given?
|
39
|
+
$env.capture_block(&block)
|
40
|
+
elsif cmd.size > 0 && (which(cmd[0]) || ($env.alias?(m) && !$env.aliasing_disabled))
|
41
|
+
$env.capture_command(cmd[0].to_s, *cmd[1..])
|
42
|
+
else
|
43
|
+
raise ArgumentError.new("nothing to capture from")
|
44
|
+
end
|
45
|
+
end
|
data/lib/rash/ext/filesystem.rb
CHANGED
@@ -6,30 +6,56 @@ class Environment
|
|
6
6
|
def initialize
|
7
7
|
common_init
|
8
8
|
@working_directory = Directory.root("/")
|
9
|
-
traverse_filetree("/", Dir.pwd)
|
10
9
|
end
|
11
10
|
|
12
11
|
def chdir(dir = nil)
|
13
12
|
old = @working_directory
|
14
|
-
traverse_filetree(
|
13
|
+
traverse_filetree(@working_directory.to_s, (dir.nil? ? "~" : dir.to_s))
|
15
14
|
ENV["OLDPWD"] = old.to_s
|
15
|
+
ENV["PWD"] = Dir.pwd
|
16
16
|
Dir.pwd
|
17
17
|
end
|
18
18
|
|
19
|
-
def local_def(name, &block)
|
20
|
-
@working_directory.add_local_method(name
|
19
|
+
def local_def(name, locked: false, &block)
|
20
|
+
@working_directory.add_local_method(name, &block)
|
21
|
+
@working_directory.lock_method(name) if locked
|
22
|
+
name.to_sym
|
21
23
|
end
|
22
24
|
|
23
25
|
def local_undef(name)
|
24
|
-
@working_directory.clear_local_method(name
|
26
|
+
@working_directory.clear_local_method(name)
|
25
27
|
end
|
26
28
|
|
27
29
|
def local_method?(name)
|
28
|
-
@working_directory.
|
30
|
+
@working_directory.local_method?(name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def local_methods
|
34
|
+
@working_directory.local_methods
|
29
35
|
end
|
30
36
|
|
31
37
|
def local_call(name, *args, &block)
|
32
|
-
@working_directory.
|
38
|
+
@working_directory.local_method(name).call(*args, &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
def local_var(name, v = nil, locked: false)
|
42
|
+
res = nil
|
43
|
+
if v.nil?
|
44
|
+
res = @working_directory.local_variable(name)
|
45
|
+
else
|
46
|
+
@working_directory.set_local_variable(name, v)
|
47
|
+
res = v
|
48
|
+
end
|
49
|
+
@working_directory.lock_variable(name) if locked
|
50
|
+
res
|
51
|
+
end
|
52
|
+
|
53
|
+
def local_var?(name)
|
54
|
+
@working_directory.local_variable?(name)
|
55
|
+
end
|
56
|
+
|
57
|
+
def local_vars
|
58
|
+
@working_directory.local_variables
|
33
59
|
end
|
34
60
|
|
35
61
|
private
|
@@ -65,7 +91,6 @@ class Environment
|
|
65
91
|
end
|
66
92
|
|
67
93
|
class Directory
|
68
|
-
attr_reader :local_methods
|
69
94
|
attr_reader :parent, :children
|
70
95
|
|
71
96
|
def self.root(dir)
|
@@ -76,9 +101,121 @@ class Environment
|
|
76
101
|
@path = Dir.new(dir)
|
77
102
|
@parent = parent
|
78
103
|
@children = []
|
79
|
-
@local_methods =
|
104
|
+
@local_methods = {}
|
105
|
+
@locked_methods = []
|
106
|
+
@local_variables = {}
|
107
|
+
@locked_variables = []
|
108
|
+
end
|
109
|
+
|
110
|
+
######################
|
111
|
+
# Local method methods
|
112
|
+
######################
|
113
|
+
|
114
|
+
def local_method(name)
|
115
|
+
name = name.to_sym
|
116
|
+
@local_methods[name] || @parent&.unlocked_local_method(name)
|
117
|
+
end
|
118
|
+
|
119
|
+
def local_method?(name)
|
120
|
+
name = name.to_sym
|
121
|
+
@local_methods.key?(name) || !!@parent&.unlocked_local_method?(name)
|
122
|
+
end
|
123
|
+
|
124
|
+
def local_methods
|
125
|
+
@local_methods.keys + (@parent&.unlocked_local_methods).to_a
|
126
|
+
end
|
127
|
+
|
128
|
+
def add_local_method(name, &block)
|
129
|
+
raise ArgumentError.new "no method body provided" unless block_given?
|
130
|
+
@local_methods[name.to_sym] = block # if name already exists, its function is overriden
|
131
|
+
name.to_sym
|
132
|
+
end
|
133
|
+
|
134
|
+
def unlocked_local_method(name)
|
135
|
+
name = name.to_sym
|
136
|
+
@local_methods[name] || @parent&.unlocked_local_method(name)
|
137
|
+
end
|
138
|
+
|
139
|
+
def unlocked_local_method?(name)
|
140
|
+
name = name.to_sym
|
141
|
+
@local_methods.filter{|k, v| !@locked_methods.include?(k)}.key?(name) ||
|
142
|
+
!!@parent&.unlocked_local_method?(name)
|
143
|
+
end
|
144
|
+
|
145
|
+
def unlocked_local_methods
|
146
|
+
@local_methods.filter{|k, v| !@locked_methods.include?(k)}.keys + (@parent&.unlocked_local_methods).to_a
|
147
|
+
end
|
148
|
+
|
149
|
+
def lock_method(name)
|
150
|
+
n = name.to_sym
|
151
|
+
raise NameError.new("#{name} is not a local method", n) unless @local_methods.key?(n)
|
152
|
+
@locked_methods << n unless @locked_methods.include?(n)
|
153
|
+
n
|
154
|
+
end
|
155
|
+
|
156
|
+
# might not be useful
|
157
|
+
def clear_local_method(name)
|
158
|
+
@local_methods.delete(name.to_sym)
|
159
|
+
name.to_sym
|
160
|
+
end
|
161
|
+
|
162
|
+
######################
|
163
|
+
# Local variable stuff
|
164
|
+
######################
|
165
|
+
|
166
|
+
def local_variable(name)
|
167
|
+
name = name.to_sym
|
168
|
+
@local_variables[name] || @parent&.unlocked_local_variable(name)
|
169
|
+
end
|
170
|
+
|
171
|
+
def local_variable?(name)
|
172
|
+
@local_variables.include?(name.to_sym) || !!@parent&.unlocked_local_variable?(name.to_sym)
|
173
|
+
end
|
174
|
+
|
175
|
+
def local_variables
|
176
|
+
@local_variables.keys + (@parent&.unlocked_local_variables).to_a
|
177
|
+
end
|
178
|
+
|
179
|
+
def set_local_variable(name, value)
|
180
|
+
name = name.to_sym
|
181
|
+
if !@local_variables.key?(name) && @parent&.unlocked_local_variable?(name)
|
182
|
+
@parent&.set_local_variable(name, value)
|
183
|
+
else
|
184
|
+
@local_variables[name] = value
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def unlocked_local_variable(name)
|
189
|
+
name = name.to_sym
|
190
|
+
@local_variables.filter{|k| !@locked_variables.include?(k)}[name] || @parent&.unlocked_local_variable(name)
|
191
|
+
end
|
192
|
+
|
193
|
+
def unlocked_local_variables
|
194
|
+
@local_variables.keys.filter{|k| !@locked_variables.include?(k)} + (@parent&.unlocked_local_variables).to_a
|
80
195
|
end
|
81
196
|
|
197
|
+
def unlocked_local_variable?(name)
|
198
|
+
name = name.to_sym
|
199
|
+
@local_variables.filter{|k,_v| !@locked_variables.include?(k)}.key?(name) ||
|
200
|
+
!!@parent&.unlocked_local_variable?(name)
|
201
|
+
end
|
202
|
+
|
203
|
+
def lock_variable(name)
|
204
|
+
n = name.to_sym
|
205
|
+
raise NameError.new("#{name} is not a local variable", n) unless @local_variables.key?(n)
|
206
|
+
@locked_variables << n unless @locked_variables.include?(n)
|
207
|
+
n
|
208
|
+
end
|
209
|
+
|
210
|
+
def clear_local_variable(name)
|
211
|
+
@local_variables.delete(name.to_sym)
|
212
|
+
name.to_sym
|
213
|
+
end
|
214
|
+
|
215
|
+
###########################
|
216
|
+
# Generic traversal methods
|
217
|
+
###########################
|
218
|
+
|
82
219
|
def root?
|
83
220
|
parent.nil?
|
84
221
|
end
|
@@ -101,18 +238,6 @@ class Environment
|
|
101
238
|
dir
|
102
239
|
end
|
103
240
|
|
104
|
-
def add_local_method(name, &block)
|
105
|
-
raise ArgumentError.new "no method body provided" unless block_given?
|
106
|
-
@local_methods[name] = block # if name already exists, its function is overriden
|
107
|
-
name
|
108
|
-
end
|
109
|
-
|
110
|
-
# might not be useful
|
111
|
-
def clear_local_method(name)
|
112
|
-
@local_methods.delete(name)
|
113
|
-
name
|
114
|
-
end
|
115
|
-
|
116
241
|
def to_s
|
117
242
|
@path.path
|
118
243
|
end
|
@@ -121,10 +246,9 @@ end
|
|
121
246
|
|
122
247
|
# still could absolutely be more cleaned up, but it works
|
123
248
|
def self.method_missing(m, *args, &block)
|
124
|
-
exe = which(m.to_s)
|
125
249
|
if $env.local_method?(m)
|
126
250
|
$env.local_call(m, *args, &block)
|
127
|
-
elsif
|
251
|
+
elsif which(m.to_s) || ($env.alias?(m) && !$env.aliasing_disabled)
|
128
252
|
$env.dispatch(m, *args)
|
129
253
|
else
|
130
254
|
super
|
@@ -132,3 +256,4 @@ def self.method_missing(m, *args, &block)
|
|
132
256
|
end
|
133
257
|
|
134
258
|
$env = Environment.new
|
259
|
+
$env.chdir(Dir.pwd)
|
data/lib/rash/jobcontrol.rb
CHANGED
data/lib/rash/pipeline.rb
CHANGED
@@ -4,6 +4,10 @@ class Environment
|
|
4
4
|
@in_pipeline
|
5
5
|
end
|
6
6
|
|
7
|
+
def synced_pipeline?
|
8
|
+
@in_pipeline && @synchronous_pipeline
|
9
|
+
end
|
10
|
+
|
7
11
|
def make_pipeline(&block)
|
8
12
|
raise IOError.new("pipelining already enabled") if @in_pipeline
|
9
13
|
start_pipeline
|
@@ -14,9 +18,21 @@ class Environment
|
|
14
18
|
end
|
15
19
|
nil
|
16
20
|
end
|
21
|
+
|
22
|
+
def make_sync_pipeline(&block)
|
23
|
+
raise IOError.new("pipelining already enabled") if @in_pipeline
|
24
|
+
start_sync_pipeline
|
25
|
+
begin
|
26
|
+
block.call
|
27
|
+
ensure
|
28
|
+
end_sync_pipeline
|
29
|
+
end
|
30
|
+
nil
|
31
|
+
end
|
17
32
|
|
18
33
|
def as_pipe_command(&block)
|
19
34
|
raise IOError.new("pipelining not enabled") unless @in_pipeline
|
35
|
+
return as_sync_pipe_command(&block) if @synchronous_pipeline
|
20
36
|
|
21
37
|
input = (@active_pipelines.empty? ? $stdin : @active_pipelines.last.reader)
|
22
38
|
@active_pipelines << Pipeline.new
|
@@ -38,28 +54,81 @@ class Environment
|
|
38
54
|
nil
|
39
55
|
end
|
40
56
|
|
57
|
+
def as_sync_pipe_command(&block)
|
58
|
+
raise IOError.new("pipelining not enabled") unless @in_pipeline
|
59
|
+
raise IOError.new("pipeline is not synchronous") unless @synchronous_pipeline
|
60
|
+
|
61
|
+
@next_pipe.close
|
62
|
+
@next_pipe = Pipeline.new # flush the output pipe
|
63
|
+
@prev_pipe.writer.close
|
64
|
+
|
65
|
+
input = (@first_sync_command ? $stdin : @prev_pipe.reader)
|
66
|
+
@first_sync_command = false
|
67
|
+
output = @next_pipe.writer
|
68
|
+
error = ($stderr == $stdout ? @next_pipe.writer : $stdin)
|
69
|
+
|
70
|
+
pid = fork do
|
71
|
+
@in_pipeline = false
|
72
|
+
@synchronous_pipeline = false
|
73
|
+
$stdin = input
|
74
|
+
$stdout = output
|
75
|
+
$stderr = error
|
76
|
+
block.call
|
77
|
+
exit!(true)
|
78
|
+
end
|
79
|
+
|
80
|
+
Process.wait(pid)
|
81
|
+
@prev_pipe, @next_pipe = @next_pipe, @prev_pipe
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
|
41
85
|
private
|
42
86
|
|
43
87
|
def start_pipeline
|
44
88
|
@in_pipeline = true
|
45
89
|
end
|
46
90
|
|
91
|
+
def start_sync_pipeline
|
92
|
+
@in_pipeline = true
|
93
|
+
@synchronous_pipeline = true
|
94
|
+
@first_sync_command = true
|
95
|
+
@prev_pipe = Pipeline.new
|
96
|
+
@next_pipe = Pipeline.new
|
97
|
+
end
|
98
|
+
|
47
99
|
def end_pipeline
|
48
100
|
raise IOError.new("pipelining not enabled") unless @in_pipeline
|
49
101
|
@in_pipeline = false
|
50
102
|
if @active_pipelines.size > 0
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
103
|
+
begin
|
104
|
+
Process.wait(@active_pipelines.last.pid)
|
105
|
+
@active_pipelines.last.writer.close # probably redundant, but leaving it for now
|
106
|
+
IO.copy_stream(@active_pipelines.last.reader, $stdout)
|
107
|
+
@active_pipelines.pop.close
|
108
|
+
@active_pipelines.reverse_each {|pipe| pipe.terminate}
|
109
|
+
ensure
|
110
|
+
@active_pipelines.clear
|
111
|
+
end
|
57
112
|
end
|
58
113
|
end
|
59
114
|
|
115
|
+
def end_sync_pipeline
|
116
|
+
raise IOError.new("pipelining not enabled") unless @in_pipeline
|
117
|
+
raise IOError.new("pipeline is not synchronous") unless @synchronous_pipeline
|
118
|
+
@next_pipe.close
|
119
|
+
@prev_pipe.writer.close
|
120
|
+
IO.copy_stream(@prev_pipe.reader, $stdout)
|
121
|
+
@prev_pipe.close
|
122
|
+
|
123
|
+
@next_pipe = @prev_pipe = @first_sync_command = nil
|
124
|
+
@synchronous_pipeline = @in_pipeline = false
|
125
|
+
end
|
126
|
+
|
60
127
|
# special method to be referenced from Environment#dispatch. Do not use directly
|
61
128
|
def add_pipeline(m, *args)
|
62
129
|
raise IOError.new("pipelining not enabled") unless @in_pipeline
|
130
|
+
return add_sync_pipeline(m, *args) if @synchronous_pipeline
|
131
|
+
|
63
132
|
input = (@active_pipelines.empty? ? $stdin : @active_pipelines.last.reader)
|
64
133
|
@active_pipelines << Pipeline.new
|
65
134
|
output = @active_pipelines.last.writer
|
@@ -73,6 +142,23 @@ class Environment
|
|
73
142
|
@active_pipelines.last.link_process(pid)
|
74
143
|
end
|
75
144
|
|
145
|
+
def add_sync_pipeline(m, *args)
|
146
|
+
raise IOError.new("pipelining not enabled") unless @in_pipeline
|
147
|
+
raise IOError.new("pipeline is not synchronous") unless @synchronous_pipeline
|
148
|
+
|
149
|
+
# Ensure pipe is empty for writing
|
150
|
+
@next_pipe.close
|
151
|
+
@next_pipe = Pipeline.new
|
152
|
+
@prev_pipe.writer.close
|
153
|
+
|
154
|
+
input = (@first_sync_command ? $stdin : @prev_pipe.reader)
|
155
|
+
@first_sync_command = false
|
156
|
+
error = ($stderr == $stdout ? @next_pipe.writer : $stdin)
|
157
|
+
system_command(m, *args, out: @next_pipe.writer, input: input, err: error, except: true)
|
158
|
+
@prev_pipe, @next_pipe = @next_pipe, @prev_pipe
|
159
|
+
nil
|
160
|
+
end
|
161
|
+
|
76
162
|
class Pipeline
|
77
163
|
attr_reader :writer, :reader, :pid
|
78
164
|
|
@@ -92,7 +178,7 @@ class Environment
|
|
92
178
|
|
93
179
|
def terminate
|
94
180
|
self.close
|
95
|
-
Process.kill(:
|
181
|
+
Process.kill(:TERM, @pid)
|
96
182
|
Process.wait(@pid)
|
97
183
|
end
|
98
184
|
|
@@ -103,6 +189,10 @@ class Environment
|
|
103
189
|
end
|
104
190
|
|
105
191
|
|
106
|
-
def in_pipeline(&block)
|
107
|
-
|
192
|
+
def in_pipeline(async: true, &block)
|
193
|
+
if async
|
194
|
+
$env.make_pipeline(&block)
|
195
|
+
else
|
196
|
+
$env.make_sync_pipeline(&block)
|
197
|
+
end
|
108
198
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rash-command-shell
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kellen Watt
|
@@ -24,7 +24,7 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.2'
|
27
|
-
description: A Ruby-based shell
|
27
|
+
description: A Ruby-based command shell
|
28
28
|
email:
|
29
29
|
executables:
|
30
30
|
- rash
|
@@ -34,6 +34,7 @@ files:
|
|
34
34
|
- bin/rash
|
35
35
|
- lib/rash.rb
|
36
36
|
- lib/rash/aliasing.rb
|
37
|
+
- lib/rash/capturing.rb
|
37
38
|
- lib/rash/ext/filesystem.rb
|
38
39
|
- lib/rash/jobcontrol.rb
|
39
40
|
- lib/rash/pipeline.rb
|