rash-command-shell 0.2.0 → 0.2.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 +3 -0
- data/lib/rash.rb +30 -10
- data/lib/rash/aliasing.rb +26 -19
- data/lib/rash/ext/filesystem.rb +3 -7
- data/lib/rash/jobcontrol.rb +0 -2
- data/lib/rash/pipeline.rb +41 -28
- data/lib/rash/redirection.rb +4 -2
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8d2870af889b41fda5f16c62f4f181c09ae843cc61ef4391596078c723c13269
|
4
|
+
data.tar.gz: 0e4e282af5b0940e63478b62985238e427969f2d95e7fccb6e25afc7f18f7c3b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ca2658254fe315de5c6902a301d1a5d7f210e68630f9ac0677d811af697a2b334e6f185882d4ac8e725e56ee3e8dad777f8b09e7a5db9159914fb2dbecf9a589
|
7
|
+
data.tar.gz: 2820b7e820e1a28fdf87e898cc284b8c8a9be53193fd72047b61cc6036ba51eb2fa92e2b0dc687e73f258a8af7854a89320230bf349b71cc8d5d3e09ee46ca62
|
data/bin/rash
CHANGED
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
if ARGV.empty?
|
4
4
|
exec("irb", "-r", "rash", *ARGV)
|
5
|
+
elsif ARGV[0] =~ /(-)?-v(ersion)?/
|
6
|
+
puts "Rash (c) 2020 Kellen Watt"
|
7
|
+
puts "Version 0.2.2" # I may forget to update this
|
5
8
|
elsif File.exists?(ARGV[0]) && !File.directory?(ARGV[0])
|
6
9
|
require "rash"
|
7
10
|
file = ARGV.shift
|
data/lib/rash.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
class Environment
|
2
2
|
|
3
3
|
attr_reader :aliasing_disabled
|
4
|
-
attr_reader :superuser_mode
|
5
4
|
attr_reader :umask
|
6
5
|
|
7
6
|
def initialize
|
@@ -56,6 +55,14 @@ class Environment
|
|
56
55
|
limits.each {|resource, limit| Process.setrlimit(resource, *limit)}
|
57
56
|
end
|
58
57
|
end
|
58
|
+
|
59
|
+
def dispatch(m, *args)
|
60
|
+
if @in_pipeline
|
61
|
+
add_pipeline(m, *args)
|
62
|
+
else
|
63
|
+
system_command(m, *args)
|
64
|
+
end
|
65
|
+
end
|
59
66
|
|
60
67
|
private
|
61
68
|
|
@@ -75,6 +82,23 @@ class Environment
|
|
75
82
|
}
|
76
83
|
ENV["RASHDIR"] = File.dirname(__FILE__)
|
77
84
|
end
|
85
|
+
|
86
|
+
def resolve_command(m, *args, literal: false)
|
87
|
+
(literal ? [m.to_s] : resolve_alias(m)) + args.flatten.map{|a| a.to_s}
|
88
|
+
end
|
89
|
+
|
90
|
+
def system_command(m, *args, except: false, literal: false, out: nil, input: nil, err: nil)
|
91
|
+
command = resolve_command(m, *args, literal: literal)
|
92
|
+
command.unshift("sudo") if @superuser_mode
|
93
|
+
opts = {out: out || $stdout,
|
94
|
+
err: err || $stderr,
|
95
|
+
in: input || $stdin,
|
96
|
+
exception: except || @superuser_mode,
|
97
|
+
umask: @umask}
|
98
|
+
|
99
|
+
system(*command, opts)
|
100
|
+
end
|
101
|
+
|
78
102
|
end
|
79
103
|
|
80
104
|
require_relative "rash/redirection"
|
@@ -103,13 +127,16 @@ def run(file, *args)
|
|
103
127
|
unless File.executable?(exe)
|
104
128
|
raise SystemCallError.new("No such executable file - #{exe}", Errno::ENOENT::Errno)
|
105
129
|
end
|
106
|
-
|
130
|
+
$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})
|
107
132
|
end
|
108
133
|
|
109
134
|
alias cmd __send__
|
110
135
|
|
111
136
|
# Defines `bash` psuedo-compatibility. Filesystem effects happen like normal
|
112
137
|
# and environmental variable changes are copied
|
138
|
+
#
|
139
|
+
# This is an artifact of an old design and is deprecated until further notice.
|
113
140
|
def sourcesh(file)
|
114
141
|
bash_env = lambda do |cmd = nil|
|
115
142
|
tmpenv = `#{cmd + ';' if cmd} printenv`
|
@@ -141,17 +168,10 @@ end
|
|
141
168
|
|
142
169
|
# Note that I defy convention and don't define `respond_to_missing?`. This
|
143
170
|
# is because doing so screws with irb.
|
144
|
-
# This code is a nightmarish monstrosity. I need some kind of "dispatch" method on Environment
|
145
171
|
def self.method_missing(m, *args, &block)
|
146
172
|
exe = which(m.to_s)
|
147
173
|
if exe || ($env.alias?(m) && !$env.aliasing_disabled)
|
148
|
-
|
149
|
-
system("sudo", *$env.resolve_alias(m), *args.flatten.map{|a| a.to_s}, {out: $stdout, err: $stderr, in: $stdin, exception: true, umask: $env.umask})
|
150
|
-
elsif $env.pipelined? # implicitly disallowing superuser_mode for now. Need to refactor to allow
|
151
|
-
$env.add_pipeline(m, *args)
|
152
|
-
else
|
153
|
-
system(*$env.resolve_alias(m), *args.flatten.map{|a| a.to_s}, {out: $stdout, err: $stderr, in: $stdin, umask: $env.umask})
|
154
|
-
end
|
174
|
+
$env.dispatch(m, *args)
|
155
175
|
else
|
156
176
|
super
|
157
177
|
end
|
data/lib/rash/aliasing.rb
CHANGED
@@ -7,26 +7,12 @@ class Environment
|
|
7
7
|
@aliases.delete(func.to_sym)
|
8
8
|
end
|
9
9
|
|
10
|
-
# recursive aliases not currently possible. In the works
|
11
|
-
def resolve_alias(f)
|
12
|
-
result = [f.to_s]
|
13
|
-
aliases = @aliases.dup
|
14
|
-
found_alias = true
|
15
|
-
while found_alias
|
16
|
-
found_alias = false
|
17
|
-
if aliases.has_key?(result[0].to_sym)
|
18
|
-
found_alias = true
|
19
|
-
match = result[0].to_sym
|
20
|
-
result[0] = aliases[match]
|
21
|
-
aliases.delete(match)
|
22
|
-
result.flatten!
|
23
|
-
end
|
24
|
-
end
|
25
|
-
result
|
26
|
-
end
|
27
|
-
|
28
10
|
def alias?(f)
|
29
|
-
@aliases.
|
11
|
+
@aliases.key?(f.to_sym)
|
12
|
+
end
|
13
|
+
|
14
|
+
def aliases
|
15
|
+
@aliases.dup
|
30
16
|
end
|
31
17
|
|
32
18
|
def without_aliasing
|
@@ -52,4 +38,25 @@ class Environment
|
|
52
38
|
end
|
53
39
|
end
|
54
40
|
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# Unless given a compelling reason, this doesn't need to be public. For most
|
45
|
+
# purposes, some combination of `alias?` and `aliases` should be sufficient.
|
46
|
+
def resolve_alias(f)
|
47
|
+
result = [f.to_s]
|
48
|
+
aliases = @aliases.dup
|
49
|
+
found_alias = true
|
50
|
+
while found_alias
|
51
|
+
found_alias = false
|
52
|
+
if aliases.has_key?(result[0].to_sym)
|
53
|
+
found_alias = true
|
54
|
+
match = result[0].to_sym
|
55
|
+
result[0] = aliases[match]
|
56
|
+
aliases.delete(match)
|
57
|
+
result.flatten!
|
58
|
+
end
|
59
|
+
end
|
60
|
+
result
|
61
|
+
end
|
55
62
|
end
|
data/lib/rash/ext/filesystem.rb
CHANGED
@@ -2,6 +2,7 @@ class Environment
|
|
2
2
|
|
3
3
|
RASH_LOCAL_FILE = ".rashrc.local"
|
4
4
|
|
5
|
+
# Has to be a better way of adding extensions and dynamically loading them
|
5
6
|
def initialize
|
6
7
|
common_init
|
7
8
|
@working_directory = Directory.root("/")
|
@@ -33,8 +34,6 @@ class Environment
|
|
33
34
|
|
34
35
|
private
|
35
36
|
|
36
|
-
LOCAL_FUNCTIONS_ENABLED = true
|
37
|
-
|
38
37
|
# from and to are strings
|
39
38
|
def traverse_filetree(from, to)
|
40
39
|
abs_from = File.expand_path(from)
|
@@ -120,16 +119,13 @@ class Environment
|
|
120
119
|
end
|
121
120
|
end
|
122
121
|
|
122
|
+
# still could absolutely be more cleaned up, but it works
|
123
123
|
def self.method_missing(m, *args, &block)
|
124
124
|
exe = which(m.to_s)
|
125
125
|
if $env.local_method?(m)
|
126
126
|
$env.local_call(m, *args, &block)
|
127
127
|
elsif exe || ($env.alias?(m) && !$env.aliasing_disabled)
|
128
|
-
|
129
|
-
system("sudo", *$env.resolve_alias(m), *args.flatten.map{|a| a.to_s}, {out: $stdout, err: $stderr, in: $stdin, exception: true, umask: $env.umask})
|
130
|
-
else
|
131
|
-
system(*$env.resolve_alias(m), *args.flatten.map{|a| a.to_s}, {out: $stdout, err: $stderr, in: $stdin, umask: $env.umask})
|
132
|
-
end
|
128
|
+
$env.dispatch(m, *args)
|
133
129
|
else
|
134
130
|
super
|
135
131
|
end
|
data/lib/rash/jobcontrol.rb
CHANGED
data/lib/rash/pipeline.rb
CHANGED
@@ -4,11 +4,48 @@ class Environment
|
|
4
4
|
@in_pipeline
|
5
5
|
end
|
6
6
|
|
7
|
+
def make_pipeline(&block)
|
8
|
+
raise IOError.new("pipelining already enabled") if @in_pipeline
|
9
|
+
start_pipeline
|
10
|
+
begin
|
11
|
+
block.call
|
12
|
+
ensure
|
13
|
+
end_pipeline
|
14
|
+
end
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def as_pipe_command(&block)
|
19
|
+
raise IOError.new("pipelining not enabled") unless @in_pipeline
|
20
|
+
|
21
|
+
input = (@active_pipelines.empty? ? $stdin : @active_pipelines.last.reader)
|
22
|
+
@active_pipelines << Pipeline.new
|
23
|
+
output = @active_pipelines.last.writer
|
24
|
+
error = ($stderr == $stdout ? output : $stderr)
|
25
|
+
|
26
|
+
pid = fork do
|
27
|
+
@in_pipeline = false
|
28
|
+
$stdin = input
|
29
|
+
$stdout = output
|
30
|
+
$stderr = error
|
31
|
+
block.call
|
32
|
+
output.close
|
33
|
+
exit!(true)
|
34
|
+
end
|
35
|
+
output.close
|
36
|
+
|
37
|
+
@active_pipelines.last.link_process(pid)
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
7
43
|
def start_pipeline
|
8
44
|
@in_pipeline = true
|
9
45
|
end
|
10
46
|
|
11
47
|
def end_pipeline
|
48
|
+
raise IOError.new("pipelining not enabled") unless @in_pipeline
|
12
49
|
@in_pipeline = false
|
13
50
|
if @active_pipelines.size > 0
|
14
51
|
Process.wait(@active_pipelines.last.pid)
|
@@ -20,13 +57,15 @@ class Environment
|
|
20
57
|
end
|
21
58
|
end
|
22
59
|
|
60
|
+
# special method to be referenced from Environment#dispatch. Do not use directly
|
23
61
|
def add_pipeline(m, *args)
|
62
|
+
raise IOError.new("pipelining not enabled") unless @in_pipeline
|
24
63
|
input = (@active_pipelines.empty? ? $stdin : @active_pipelines.last.reader)
|
25
64
|
@active_pipelines << Pipeline.new
|
26
65
|
output = @active_pipelines.last.writer
|
27
66
|
error = ($stderr == $stdout ? output : $stderr)
|
28
67
|
pid = fork do # might not be necessary, spawn might cover it. Not risking it before testing
|
29
|
-
|
68
|
+
system_command(m, *args, out: output, input: input, err: error, except: true)
|
30
69
|
output.close
|
31
70
|
exit!(true)
|
32
71
|
end
|
@@ -34,26 +73,6 @@ class Environment
|
|
34
73
|
@active_pipelines.last.link_process(pid)
|
35
74
|
end
|
36
75
|
|
37
|
-
def as_pipe(&block)
|
38
|
-
input = (@active_pipelines.empty? ? $stdin : @active_pipelines.last.reader)
|
39
|
-
@active_pipelines << Pipeline.new
|
40
|
-
output = @active_pipelines.last.writer
|
41
|
-
error = ($stderr == $stdout ? output : $stderr)
|
42
|
-
pid = fork do
|
43
|
-
@in_pipeline = false
|
44
|
-
$stdin = input
|
45
|
-
$stdout = output
|
46
|
-
$stderr = error
|
47
|
-
block.call
|
48
|
-
output.close
|
49
|
-
exit!(true)
|
50
|
-
end
|
51
|
-
output.close
|
52
|
-
@active_pipelines.last.link_process(pid)
|
53
|
-
end
|
54
|
-
|
55
|
-
private
|
56
|
-
|
57
76
|
class Pipeline
|
58
77
|
attr_reader :writer, :reader, :pid
|
59
78
|
|
@@ -85,11 +104,5 @@ end
|
|
85
104
|
|
86
105
|
|
87
106
|
def in_pipeline(&block)
|
88
|
-
|
89
|
-
$env.start_pipeline
|
90
|
-
begin
|
91
|
-
block.call
|
92
|
-
ensure
|
93
|
-
$env.end_pipeline
|
94
|
-
end
|
107
|
+
$env.make_pipeline(&block)
|
95
108
|
end
|
data/lib/rash/redirection.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
class Environment
|
2
2
|
|
3
|
-
DEFAULT_IO = {in: STDIN, out: STDOUT, err: STDERR}
|
4
|
-
|
5
3
|
def reset_io
|
6
4
|
reset_stdout
|
7
5
|
reset_stderr
|
@@ -78,6 +76,10 @@ class Environment
|
|
78
76
|
def standard_stream?(f)
|
79
77
|
DEFAULT_IO.values.include?(f)
|
80
78
|
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
DEFAULT_IO = {in: STDIN, out: STDOUT, err: STDERR}
|
81
83
|
end
|
82
84
|
|
83
85
|
# If you want to append, you need to get the file object yourself.
|