shell 0.0.1 → 0.7
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 +7 -0
- data/.gitignore +8 -4
- data/.travis.yml +6 -0
- data/Gemfile +4 -2
- data/LICENSE.txt +22 -0
- data/README.md +90 -8
- data/Rakefile +7 -5
- data/bin/console +7 -0
- data/bin/setup +6 -0
- data/lib/shell.rb +462 -0
- data/lib/shell/builtin-command.rb +147 -0
- data/lib/shell/command-processor.rb +668 -0
- data/lib/shell/error.rb +26 -0
- data/lib/shell/filter.rb +138 -0
- data/lib/shell/process-controller.rb +309 -0
- data/lib/shell/system-command.rb +159 -0
- data/lib/shell/version.rb +17 -0
- data/shell.gemspec +21 -20
- metadata +46 -41
- data/lib/shell_utils.rb +0 -58
- data/lib/shell_utils/version.rb +0 -3
- data/test/shell_utils_test.rb +0 -25
- data/test/test_helper.rb +0 -4
@@ -0,0 +1,147 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
#
|
3
|
+
# shell/builtin-command.rb -
|
4
|
+
# $Release Version: 0.7 $
|
5
|
+
# $Revision$
|
6
|
+
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
|
7
|
+
#
|
8
|
+
# --
|
9
|
+
#
|
10
|
+
#
|
11
|
+
#
|
12
|
+
|
13
|
+
require_relative "filter"
|
14
|
+
|
15
|
+
class Shell
|
16
|
+
class BuiltInCommand < Filter
|
17
|
+
def wait?
|
18
|
+
false
|
19
|
+
end
|
20
|
+
def active?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Void < BuiltInCommand
|
26
|
+
def initialize(sh, *opts)
|
27
|
+
super sh
|
28
|
+
end
|
29
|
+
|
30
|
+
def each(rs = nil)
|
31
|
+
# do nothing
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Echo < BuiltInCommand
|
36
|
+
def initialize(sh, *strings)
|
37
|
+
super sh
|
38
|
+
@strings = strings
|
39
|
+
end
|
40
|
+
|
41
|
+
def each(rs = nil)
|
42
|
+
rs = @shell.record_separator unless rs
|
43
|
+
for str in @strings
|
44
|
+
yield str + rs
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Cat < BuiltInCommand
|
50
|
+
def initialize(sh, *filenames)
|
51
|
+
super sh
|
52
|
+
@cat_files = filenames
|
53
|
+
end
|
54
|
+
|
55
|
+
def each(rs = nil)
|
56
|
+
if @cat_files.empty?
|
57
|
+
super
|
58
|
+
else
|
59
|
+
for src in @cat_files
|
60
|
+
@shell.foreach(src, rs){|l| yield l}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class Glob < BuiltInCommand
|
67
|
+
def initialize(sh, pattern)
|
68
|
+
super sh
|
69
|
+
|
70
|
+
@pattern = pattern
|
71
|
+
end
|
72
|
+
|
73
|
+
def each(rs = nil)
|
74
|
+
if @pattern[0] == ?/
|
75
|
+
@files = Dir[@pattern]
|
76
|
+
else
|
77
|
+
prefix = @shell.pwd+"/"
|
78
|
+
@files = Dir[prefix+@pattern].collect{|p| p.sub(prefix, "")}
|
79
|
+
end
|
80
|
+
rs = @shell.record_separator unless rs
|
81
|
+
for f in @files
|
82
|
+
yield f+rs
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class AppendIO < BuiltInCommand
|
88
|
+
def initialize(sh, io, filter)
|
89
|
+
super sh
|
90
|
+
@input = filter
|
91
|
+
@io = io
|
92
|
+
end
|
93
|
+
|
94
|
+
def input=(filter)
|
95
|
+
@input.input=filter
|
96
|
+
for l in @input
|
97
|
+
@io << l
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
class AppendFile < AppendIO
|
104
|
+
def initialize(sh, to_filename, filter)
|
105
|
+
@file_name = to_filename
|
106
|
+
io = sh.open(to_filename, "a")
|
107
|
+
super(sh, io, filter)
|
108
|
+
end
|
109
|
+
|
110
|
+
def input=(filter)
|
111
|
+
begin
|
112
|
+
super
|
113
|
+
ensure
|
114
|
+
@io.close
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class Tee < BuiltInCommand
|
120
|
+
def initialize(sh, filename)
|
121
|
+
super sh
|
122
|
+
@to_filename = filename
|
123
|
+
end
|
124
|
+
|
125
|
+
def each(rs = nil)
|
126
|
+
to = @shell.open(@to_filename, "w")
|
127
|
+
begin
|
128
|
+
super{|l| to << l; yield l}
|
129
|
+
ensure
|
130
|
+
to.close
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class Concat < BuiltInCommand
|
136
|
+
def initialize(sh, *jobs)
|
137
|
+
super(sh)
|
138
|
+
@jobs = jobs
|
139
|
+
end
|
140
|
+
|
141
|
+
def each(rs = nil)
|
142
|
+
while job = @jobs.shift
|
143
|
+
job.each{|l| yield l}
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,668 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
#
|
3
|
+
# shell/command-controller.rb -
|
4
|
+
# $Release Version: 0.7 $
|
5
|
+
# $Revision$
|
6
|
+
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
|
7
|
+
#
|
8
|
+
# --
|
9
|
+
#
|
10
|
+
#
|
11
|
+
#
|
12
|
+
|
13
|
+
require "e2mmap"
|
14
|
+
|
15
|
+
require_relative "error"
|
16
|
+
require_relative "filter"
|
17
|
+
require_relative "system-command"
|
18
|
+
require_relative "builtin-command"
|
19
|
+
|
20
|
+
class Shell
|
21
|
+
# In order to execute a command on your OS, you need to define it as a
|
22
|
+
# Shell method.
|
23
|
+
#
|
24
|
+
# Alternatively, you can execute any command via
|
25
|
+
# Shell::CommandProcessor#system even if it is not defined.
|
26
|
+
class CommandProcessor
|
27
|
+
|
28
|
+
#
|
29
|
+
# initialize of Shell and related classes.
|
30
|
+
#
|
31
|
+
m = [:initialize, :expand_path]
|
32
|
+
if Object.methods.first.kind_of?(String)
|
33
|
+
NoDelegateMethods = m.collect{|x| x.id2name}
|
34
|
+
else
|
35
|
+
NoDelegateMethods = m
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.initialize
|
39
|
+
|
40
|
+
install_builtin_commands
|
41
|
+
|
42
|
+
# define CommandProcessor#methods to Shell#methods and Filter#methods
|
43
|
+
for m in CommandProcessor.instance_methods(false) - NoDelegateMethods
|
44
|
+
add_delegate_command_to_shell(m)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.method_added(id)
|
48
|
+
add_delegate_command_to_shell(id)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# include run file.
|
54
|
+
#
|
55
|
+
def self.run_config
|
56
|
+
rc = "~/.rb_shell"
|
57
|
+
begin
|
58
|
+
load File.expand_path(rc) if ENV.key?("HOME")
|
59
|
+
rescue LoadError, Errno::ENOENT
|
60
|
+
rescue
|
61
|
+
print "load error: #{rc}\n"
|
62
|
+
print $!.class, ": ", $!, "\n"
|
63
|
+
for err in $@[0, $@.size - 2]
|
64
|
+
print "\t", err, "\n"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def initialize(shell)
|
70
|
+
@shell = shell
|
71
|
+
@system_commands = {}
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# CommandProcessor#expand_path(path)
|
76
|
+
# path: String
|
77
|
+
# return: String
|
78
|
+
# returns the absolute path for <path>
|
79
|
+
#
|
80
|
+
def expand_path(path)
|
81
|
+
@shell.expand_path(path)
|
82
|
+
end
|
83
|
+
|
84
|
+
# call-seq:
|
85
|
+
# foreach(path, record_separator) -> Enumerator
|
86
|
+
# foreach(path, record_separator) { block }
|
87
|
+
#
|
88
|
+
# See IO.foreach when +path+ is a file.
|
89
|
+
#
|
90
|
+
# See Dir.foreach when +path+ is a directory.
|
91
|
+
#
|
92
|
+
def foreach(path = nil, *rs)
|
93
|
+
path = "." unless path
|
94
|
+
path = expand_path(path)
|
95
|
+
|
96
|
+
if File.directory?(path)
|
97
|
+
Dir.foreach(path){|fn| yield fn}
|
98
|
+
else
|
99
|
+
IO.foreach(path, *rs){|l| yield l}
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# call-seq:
|
104
|
+
# open(path, mode, permissions) -> Enumerator
|
105
|
+
# open(path, mode, permissions) { block }
|
106
|
+
#
|
107
|
+
# See IO.open when +path+ is a file.
|
108
|
+
#
|
109
|
+
# See Dir.open when +path+ is a directory.
|
110
|
+
#
|
111
|
+
def open(path, mode = nil, perm = 0666, &b)
|
112
|
+
path = expand_path(path)
|
113
|
+
if File.directory?(path)
|
114
|
+
Dir.open(path, &b)
|
115
|
+
else
|
116
|
+
if @shell.umask
|
117
|
+
f = File.open(path, mode, perm)
|
118
|
+
File.chmod(perm & ~@shell.umask, path)
|
119
|
+
if block_given?
|
120
|
+
f.each(&b)
|
121
|
+
end
|
122
|
+
f
|
123
|
+
else
|
124
|
+
File.open(path, mode, perm, &b)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# call-seq:
|
130
|
+
# unlink(path)
|
131
|
+
#
|
132
|
+
# See IO.unlink when +path+ is a file.
|
133
|
+
#
|
134
|
+
# See Dir.unlink when +path+ is a directory.
|
135
|
+
#
|
136
|
+
def unlink(path)
|
137
|
+
@shell.check_point
|
138
|
+
|
139
|
+
path = expand_path(path)
|
140
|
+
if File.directory?(path)
|
141
|
+
Dir.unlink(path)
|
142
|
+
else
|
143
|
+
IO.unlink(path)
|
144
|
+
end
|
145
|
+
Void.new(@shell)
|
146
|
+
end
|
147
|
+
|
148
|
+
# See Shell::CommandProcessor#test
|
149
|
+
alias top_level_test test
|
150
|
+
# call-seq:
|
151
|
+
# test(command, file1, file2) -> true or false
|
152
|
+
# [command, file1, file2] -> true or false
|
153
|
+
#
|
154
|
+
# Tests if the given +command+ exists in +file1+, or optionally +file2+.
|
155
|
+
#
|
156
|
+
# Example:
|
157
|
+
# sh[?e, "foo"]
|
158
|
+
# sh[:e, "foo"]
|
159
|
+
# sh["e", "foo"]
|
160
|
+
# sh[:exists?, "foo"]
|
161
|
+
# sh["exists?", "foo"]
|
162
|
+
#
|
163
|
+
def test(command, file1, file2=nil)
|
164
|
+
file1 = expand_path(file1)
|
165
|
+
file2 = expand_path(file2) if file2
|
166
|
+
command = command.id2name if command.kind_of?(Symbol)
|
167
|
+
|
168
|
+
case command
|
169
|
+
when Integer
|
170
|
+
if file2
|
171
|
+
top_level_test(command, file1, file2)
|
172
|
+
else
|
173
|
+
top_level_test(command, file1)
|
174
|
+
end
|
175
|
+
when String
|
176
|
+
if command.size == 1
|
177
|
+
if file2
|
178
|
+
top_level_test(command, file1, file2)
|
179
|
+
else
|
180
|
+
top_level_test(command, file1)
|
181
|
+
end
|
182
|
+
else
|
183
|
+
if file2
|
184
|
+
FileTest.send(command, file1, file2)
|
185
|
+
else
|
186
|
+
FileTest.send(command, file1)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
# See Shell::CommandProcessor#test
|
192
|
+
alias [] test
|
193
|
+
|
194
|
+
# call-seq:
|
195
|
+
# mkdir(path)
|
196
|
+
#
|
197
|
+
# Same as Dir.mkdir, except multiple directories are allowed.
|
198
|
+
def mkdir(*path)
|
199
|
+
@shell.check_point
|
200
|
+
notify("mkdir #{path.join(' ')}")
|
201
|
+
|
202
|
+
perm = nil
|
203
|
+
if path.last.kind_of?(Integer)
|
204
|
+
perm = path.pop
|
205
|
+
end
|
206
|
+
for dir in path
|
207
|
+
d = expand_path(dir)
|
208
|
+
if perm
|
209
|
+
Dir.mkdir(d, perm)
|
210
|
+
else
|
211
|
+
Dir.mkdir(d)
|
212
|
+
end
|
213
|
+
File.chmod(d, 0666 & ~@shell.umask) if @shell.umask
|
214
|
+
end
|
215
|
+
Void.new(@shell)
|
216
|
+
end
|
217
|
+
|
218
|
+
# call-seq:
|
219
|
+
# rmdir(path)
|
220
|
+
#
|
221
|
+
# Same as Dir.rmdir, except multiple directories are allowed.
|
222
|
+
def rmdir(*path)
|
223
|
+
@shell.check_point
|
224
|
+
notify("rmdir #{path.join(' ')}")
|
225
|
+
|
226
|
+
for dir in path
|
227
|
+
Dir.rmdir(expand_path(dir))
|
228
|
+
end
|
229
|
+
Void.new(@shell)
|
230
|
+
end
|
231
|
+
|
232
|
+
# call-seq:
|
233
|
+
# system(command, *options) -> SystemCommand
|
234
|
+
#
|
235
|
+
# Executes the given +command+ with the +options+ parameter.
|
236
|
+
#
|
237
|
+
# Example:
|
238
|
+
# print sh.system("ls", "-l")
|
239
|
+
# sh.system("ls", "-l") | sh.head > STDOUT
|
240
|
+
#
|
241
|
+
def system(command, *opts)
|
242
|
+
if opts.empty?
|
243
|
+
if command =~ /\*|\?|\{|\}|\[|\]|<|>|\(|\)|~|&|\||\\|\$|;|'|`|"|\n/
|
244
|
+
return SystemCommand.new(@shell, find_system_command("sh"), "-c", command)
|
245
|
+
else
|
246
|
+
command, *opts = command.split(/\s+/)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
SystemCommand.new(@shell, find_system_command(command), *opts)
|
250
|
+
end
|
251
|
+
|
252
|
+
# call-seq:
|
253
|
+
# rehash
|
254
|
+
#
|
255
|
+
# Clears the command hash table.
|
256
|
+
def rehash
|
257
|
+
@system_commands = {}
|
258
|
+
end
|
259
|
+
|
260
|
+
def check_point # :nodoc:
|
261
|
+
@shell.process_controller.wait_all_jobs_execution
|
262
|
+
end
|
263
|
+
alias finish_all_jobs check_point # :nodoc:
|
264
|
+
|
265
|
+
# call-seq:
|
266
|
+
# transact { block }
|
267
|
+
#
|
268
|
+
# Executes a block as self
|
269
|
+
#
|
270
|
+
# Example:
|
271
|
+
# sh.transact { system("ls", "-l") | head > STDOUT }
|
272
|
+
def transact(&block)
|
273
|
+
begin
|
274
|
+
@shell.instance_eval(&block)
|
275
|
+
ensure
|
276
|
+
check_point
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
# call-seq:
|
281
|
+
# out(device) { block }
|
282
|
+
#
|
283
|
+
# Calls <code>device.print</code> on the result passing the _block_ to
|
284
|
+
# #transact
|
285
|
+
def out(dev = STDOUT, &block)
|
286
|
+
dev.print transact(&block)
|
287
|
+
end
|
288
|
+
|
289
|
+
# call-seq:
|
290
|
+
# echo(*strings) -> Echo
|
291
|
+
#
|
292
|
+
# Returns a Echo object, for the given +strings+
|
293
|
+
def echo(*strings)
|
294
|
+
Echo.new(@shell, *strings)
|
295
|
+
end
|
296
|
+
|
297
|
+
# call-seq:
|
298
|
+
# cat(*filename) -> Cat
|
299
|
+
#
|
300
|
+
# Returns a Cat object, for the given +filenames+
|
301
|
+
def cat(*filenames)
|
302
|
+
Cat.new(@shell, *filenames)
|
303
|
+
end
|
304
|
+
|
305
|
+
# def sort(*filenames)
|
306
|
+
# Sort.new(self, *filenames)
|
307
|
+
# end
|
308
|
+
# call-seq:
|
309
|
+
# glob(pattern) -> Glob
|
310
|
+
#
|
311
|
+
# Returns a Glob filter object, with the given +pattern+ object
|
312
|
+
def glob(pattern)
|
313
|
+
Glob.new(@shell, pattern)
|
314
|
+
end
|
315
|
+
|
316
|
+
def append(to, filter)
|
317
|
+
case to
|
318
|
+
when String
|
319
|
+
AppendFile.new(@shell, to, filter)
|
320
|
+
when IO
|
321
|
+
AppendIO.new(@shell, to, filter)
|
322
|
+
else
|
323
|
+
Shell.Fail Error::CantApplyMethod, "append", to.class
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
# call-seq:
|
328
|
+
# tee(file) -> Tee
|
329
|
+
#
|
330
|
+
# Returns a Tee filter object, with the given +file+ command
|
331
|
+
def tee(file)
|
332
|
+
Tee.new(@shell, file)
|
333
|
+
end
|
334
|
+
|
335
|
+
# call-seq:
|
336
|
+
# concat(*jobs) -> Concat
|
337
|
+
#
|
338
|
+
# Returns a Concat object, for the given +jobs+
|
339
|
+
def concat(*jobs)
|
340
|
+
Concat.new(@shell, *jobs)
|
341
|
+
end
|
342
|
+
|
343
|
+
# %pwd, %cwd -> @pwd
|
344
|
+
def notify(*opts)
|
345
|
+
Shell.notify(*opts) {|mes|
|
346
|
+
yield mes if iterator?
|
347
|
+
|
348
|
+
mes.gsub!("%pwd", "#{@cwd}")
|
349
|
+
mes.gsub!("%cwd", "#{@cwd}")
|
350
|
+
}
|
351
|
+
end
|
352
|
+
|
353
|
+
#
|
354
|
+
# private functions
|
355
|
+
#
|
356
|
+
def find_system_command(command)
|
357
|
+
return command if /^\// =~ command
|
358
|
+
case path = @system_commands[command]
|
359
|
+
when String
|
360
|
+
if exists?(path)
|
361
|
+
return path
|
362
|
+
else
|
363
|
+
Shell.Fail Error::CommandNotFound, command
|
364
|
+
end
|
365
|
+
when false
|
366
|
+
Shell.Fail Error::CommandNotFound, command
|
367
|
+
end
|
368
|
+
|
369
|
+
for p in @shell.system_path
|
370
|
+
path = join(p, command)
|
371
|
+
begin
|
372
|
+
st = File.stat(path)
|
373
|
+
rescue SystemCallError
|
374
|
+
next
|
375
|
+
else
|
376
|
+
next unless st.executable? and !st.directory?
|
377
|
+
@system_commands[command] = path
|
378
|
+
return path
|
379
|
+
end
|
380
|
+
end
|
381
|
+
@system_commands[command] = false
|
382
|
+
Shell.Fail Error::CommandNotFound, command
|
383
|
+
end
|
384
|
+
|
385
|
+
# call-seq:
|
386
|
+
# def_system_command(command, path) -> Shell::SystemCommand
|
387
|
+
#
|
388
|
+
# Defines a command, registering +path+ as a Shell method for the given
|
389
|
+
# +command+.
|
390
|
+
#
|
391
|
+
# Shell::CommandProcessor.def_system_command "ls"
|
392
|
+
# #=> Defines ls.
|
393
|
+
#
|
394
|
+
# Shell::CommandProcessor.def_system_command "sys_sort", "sort"
|
395
|
+
# #=> Defines sys_sort as sort
|
396
|
+
#
|
397
|
+
def self.def_system_command(command, path = command)
|
398
|
+
begin
|
399
|
+
eval((d = %Q[def #{command}(*opts)
|
400
|
+
SystemCommand.new(@shell, '#{path}', *opts)
|
401
|
+
end]), nil, __FILE__, __LINE__ - 1)
|
402
|
+
rescue SyntaxError
|
403
|
+
Shell.notify "warn: Can't define #{command} path: #{path}."
|
404
|
+
end
|
405
|
+
Shell.notify "Define #{command} path: #{path}.", Shell.debug?
|
406
|
+
Shell.notify("Definition of #{command}: ", d,
|
407
|
+
Shell.debug.kind_of?(Integer) && Shell.debug > 1)
|
408
|
+
end
|
409
|
+
|
410
|
+
# call-seq:
|
411
|
+
# undef_system_command(command) -> self
|
412
|
+
#
|
413
|
+
# Undefines a command
|
414
|
+
def self.undef_system_command(command)
|
415
|
+
command = command.id2name if command.kind_of?(Symbol)
|
416
|
+
remove_method(command)
|
417
|
+
Shell.module_eval{remove_method(command)}
|
418
|
+
Filter.module_eval{remove_method(command)}
|
419
|
+
self
|
420
|
+
end
|
421
|
+
|
422
|
+
@alias_map = {}
|
423
|
+
# Returns a list of aliased commands
|
424
|
+
def self.alias_map
|
425
|
+
@alias_map
|
426
|
+
end
|
427
|
+
# call-seq:
|
428
|
+
# alias_command(alias, command, *options) -> self
|
429
|
+
#
|
430
|
+
# Creates a command alias at the given +alias+ for the given +command+,
|
431
|
+
# passing any +options+ along with it.
|
432
|
+
#
|
433
|
+
# Shell::CommandProcessor.alias_command "lsC", "ls", "-CBF", "--show-control-chars"
|
434
|
+
# Shell::CommandProcessor.alias_command("lsC", "ls"){|*opts| ["-CBF", "--show-control-chars", *opts]}
|
435
|
+
#
|
436
|
+
def self.alias_command(ali, command, *opts)
|
437
|
+
ali = ali.id2name if ali.kind_of?(Symbol)
|
438
|
+
command = command.id2name if command.kind_of?(Symbol)
|
439
|
+
begin
|
440
|
+
if iterator?
|
441
|
+
@alias_map[ali.intern] = proc
|
442
|
+
|
443
|
+
eval((d = %Q[def #{ali}(*opts)
|
444
|
+
@shell.__send__(:#{command},
|
445
|
+
*(CommandProcessor.alias_map[:#{ali}].call *opts))
|
446
|
+
end]), nil, __FILE__, __LINE__ - 1)
|
447
|
+
|
448
|
+
else
|
449
|
+
args = opts.collect{|opt| '"' + opt + '"'}.join(",")
|
450
|
+
eval((d = %Q[def #{ali}(*opts)
|
451
|
+
@shell.__send__(:#{command}, #{args}, *opts)
|
452
|
+
end]), nil, __FILE__, __LINE__ - 1)
|
453
|
+
end
|
454
|
+
rescue SyntaxError
|
455
|
+
Shell.notify "warn: Can't alias #{ali} command: #{command}."
|
456
|
+
Shell.notify("Definition of #{ali}: ", d)
|
457
|
+
raise
|
458
|
+
end
|
459
|
+
Shell.notify "Define #{ali} command: #{command}.", Shell.debug?
|
460
|
+
Shell.notify("Definition of #{ali}: ", d,
|
461
|
+
Shell.debug.kind_of?(Integer) && Shell.debug > 1)
|
462
|
+
self
|
463
|
+
end
|
464
|
+
|
465
|
+
# call-seq:
|
466
|
+
# unalias_command(alias) -> self
|
467
|
+
#
|
468
|
+
# Unaliases the given +alias+ command.
|
469
|
+
def self.unalias_command(ali)
|
470
|
+
ali = ali.id2name if ali.kind_of?(Symbol)
|
471
|
+
@alias_map.delete ali.intern
|
472
|
+
undef_system_command(ali)
|
473
|
+
end
|
474
|
+
|
475
|
+
# :nodoc:
|
476
|
+
#
|
477
|
+
# Delegates File and FileTest methods into Shell, including the following
|
478
|
+
# commands:
|
479
|
+
#
|
480
|
+
# * Shell#blockdev?(file)
|
481
|
+
# * Shell#chardev?(file)
|
482
|
+
# * Shell#directory?(file)
|
483
|
+
# * Shell#executable?(file)
|
484
|
+
# * Shell#executable_real?(file)
|
485
|
+
# * Shell#exist?(file)/Shell#exists?(file)
|
486
|
+
# * Shell#file?(file)
|
487
|
+
# * Shell#grpowned?(file)
|
488
|
+
# * Shell#owned?(file)
|
489
|
+
# * Shell#pipe?(file)
|
490
|
+
# * Shell#readable?(file)
|
491
|
+
# * Shell#readable_real?(file)
|
492
|
+
# * Shell#setgid?(file)
|
493
|
+
# * Shell#setuid?(file)
|
494
|
+
# * Shell#size(file)/Shell#size?(file)
|
495
|
+
# * Shell#socket?(file)
|
496
|
+
# * Shell#sticky?(file)
|
497
|
+
# * Shell#symlink?(file)
|
498
|
+
# * Shell#writable?(file)
|
499
|
+
# * Shell#writable_real?(file)
|
500
|
+
# * Shell#zero?(file)
|
501
|
+
# * Shell#syscopy(filename_from, filename_to)
|
502
|
+
# * Shell#copy(filename_from, filename_to)
|
503
|
+
# * Shell#move(filename_from, filename_to)
|
504
|
+
# * Shell#compare(filename_from, filename_to)
|
505
|
+
# * Shell#safe_unlink(*filenames)
|
506
|
+
# * Shell#makedirs(*filenames)
|
507
|
+
# * Shell#install(filename_from, filename_to, mode)
|
508
|
+
#
|
509
|
+
# And also, there are some aliases for convenience:
|
510
|
+
#
|
511
|
+
# * Shell#cmp <- Shell#compare
|
512
|
+
# * Shell#mv <- Shell#move
|
513
|
+
# * Shell#cp <- Shell#copy
|
514
|
+
# * Shell#rm_f <- Shell#safe_unlink
|
515
|
+
# * Shell#mkpath <- Shell#makedirs
|
516
|
+
#
|
517
|
+
def self.def_builtin_commands(delegation_class, command_specs)
|
518
|
+
for meth, args in command_specs
|
519
|
+
arg_str = args.collect{|arg| arg.downcase}.join(", ")
|
520
|
+
call_arg_str = args.collect{
|
521
|
+
|arg|
|
522
|
+
case arg
|
523
|
+
when /^(FILENAME.*)$/
|
524
|
+
format("expand_path(%s)", $1.downcase)
|
525
|
+
when /^(\*FILENAME.*)$/
|
526
|
+
# \*FILENAME* -> filenames.collect{|fn| expand_path(fn)}.join(", ")
|
527
|
+
$1.downcase + '.collect{|fn| expand_path(fn)}'
|
528
|
+
else
|
529
|
+
arg
|
530
|
+
end
|
531
|
+
}.join(", ")
|
532
|
+
d = %Q[def #{meth}(#{arg_str})
|
533
|
+
#{delegation_class}.#{meth}(#{call_arg_str})
|
534
|
+
end]
|
535
|
+
Shell.notify "Define #{meth}(#{arg_str})", Shell.debug?
|
536
|
+
Shell.notify("Definition of #{meth}: ", d,
|
537
|
+
Shell.debug.kind_of?(Integer) && Shell.debug > 1)
|
538
|
+
eval d
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
# call-seq:
|
543
|
+
# install_system_commands(prefix = "sys_")
|
544
|
+
#
|
545
|
+
# Defines all commands in the Shell.default_system_path as Shell method,
|
546
|
+
# all with given +prefix+ appended to their names.
|
547
|
+
#
|
548
|
+
# Any invalid character names are converted to +_+, and errors are passed
|
549
|
+
# to Shell.notify.
|
550
|
+
#
|
551
|
+
# Methods already defined are skipped.
|
552
|
+
def self.install_system_commands(pre = "sys_")
|
553
|
+
defined_meth = {}
|
554
|
+
for m in Shell.methods
|
555
|
+
defined_meth[m] = true
|
556
|
+
end
|
557
|
+
sh = Shell.new
|
558
|
+
for path in Shell.default_system_path
|
559
|
+
next unless sh.directory? path
|
560
|
+
sh.cd path
|
561
|
+
sh.foreach do
|
562
|
+
|cn|
|
563
|
+
if !defined_meth[pre + cn] && sh.file?(cn) && sh.executable?(cn)
|
564
|
+
command = (pre + cn).gsub(/\W/, "_").sub(/^([0-9])/, '_\1')
|
565
|
+
begin
|
566
|
+
def_system_command(command, sh.expand_path(cn))
|
567
|
+
rescue
|
568
|
+
Shell.notify "warn: Can't define #{command} path: #{cn}"
|
569
|
+
end
|
570
|
+
defined_meth[command] = command
|
571
|
+
end
|
572
|
+
end
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
def self.add_delegate_command_to_shell(id) # :nodoc:
|
577
|
+
id = id.intern if id.kind_of?(String)
|
578
|
+
name = id.id2name
|
579
|
+
if Shell.method_defined?(id)
|
580
|
+
Shell.notify "warn: override definition of Shell##{name}."
|
581
|
+
Shell.notify "warn: alias Shell##{name} to Shell##{name}_org.\n"
|
582
|
+
Shell.module_eval "alias #{name}_org #{name}"
|
583
|
+
end
|
584
|
+
Shell.notify "method added: Shell##{name}.", Shell.debug?
|
585
|
+
Shell.module_eval(%Q[def #{name}(*args, &block)
|
586
|
+
begin
|
587
|
+
@command_processor.__send__(:#{name}, *args, &block)
|
588
|
+
rescue Exception
|
589
|
+
$@.delete_if{|s| /:in `__getobj__'$/ =~ s} #`
|
590
|
+
$@.delete_if{|s| /^\\(eval\\):/ =~ s}
|
591
|
+
raise
|
592
|
+
end
|
593
|
+
end], __FILE__, __LINE__)
|
594
|
+
|
595
|
+
if Shell::Filter.method_defined?(id)
|
596
|
+
Shell.notify "warn: override definition of Shell::Filter##{name}."
|
597
|
+
Shell.notify "warn: alias Shell##{name} to Shell::Filter##{name}_org."
|
598
|
+
Filter.module_eval "alias #{name}_org #{name}"
|
599
|
+
end
|
600
|
+
Shell.notify "method added: Shell::Filter##{name}.", Shell.debug?
|
601
|
+
Filter.module_eval(%Q[def #{name}(*args, &block)
|
602
|
+
begin
|
603
|
+
self | @shell.__send__(:#{name}, *args, &block)
|
604
|
+
rescue Exception
|
605
|
+
$@.delete_if{|s| /:in `__getobj__'$/ =~ s} #`
|
606
|
+
$@.delete_if{|s| /^\\(eval\\):/ =~ s}
|
607
|
+
raise
|
608
|
+
end
|
609
|
+
end], __FILE__, __LINE__)
|
610
|
+
end
|
611
|
+
|
612
|
+
# Delegates File methods into Shell, including the following commands:
|
613
|
+
#
|
614
|
+
# * Shell#atime(file)
|
615
|
+
# * Shell#basename(file, *opt)
|
616
|
+
# * Shell#chmod(mode, *files)
|
617
|
+
# * Shell#chown(owner, group, *file)
|
618
|
+
# * Shell#ctime(file)
|
619
|
+
# * Shell#delete(*file)
|
620
|
+
# * Shell#dirname(file)
|
621
|
+
# * Shell#ftype(file)
|
622
|
+
# * Shell#join(*file)
|
623
|
+
# * Shell#link(file_from, file_to)
|
624
|
+
# * Shell#lstat(file)
|
625
|
+
# * Shell#mtime(file)
|
626
|
+
# * Shell#readlink(file)
|
627
|
+
# * Shell#rename(file_from, file_to)
|
628
|
+
# * Shell#split(file)
|
629
|
+
# * Shell#stat(file)
|
630
|
+
# * Shell#symlink(file_from, file_to)
|
631
|
+
# * Shell#truncate(file, length)
|
632
|
+
# * Shell#utime(atime, mtime, *file)
|
633
|
+
#
|
634
|
+
def self.install_builtin_commands
|
635
|
+
# method related File.
|
636
|
+
# (exclude open/foreach/unlink)
|
637
|
+
normal_delegation_file_methods = [
|
638
|
+
["atime", ["FILENAME"]],
|
639
|
+
["basename", ["fn", "*opts"]],
|
640
|
+
["chmod", ["mode", "*FILENAMES"]],
|
641
|
+
["chown", ["owner", "group", "*FILENAME"]],
|
642
|
+
["ctime", ["FILENAMES"]],
|
643
|
+
["delete", ["*FILENAMES"]],
|
644
|
+
["dirname", ["FILENAME"]],
|
645
|
+
["ftype", ["FILENAME"]],
|
646
|
+
["join", ["*items"]],
|
647
|
+
["link", ["FILENAME_O", "FILENAME_N"]],
|
648
|
+
["lstat", ["FILENAME"]],
|
649
|
+
["mtime", ["FILENAME"]],
|
650
|
+
["readlink", ["FILENAME"]],
|
651
|
+
["rename", ["FILENAME_FROM", "FILENAME_TO"]],
|
652
|
+
["split", ["pathname"]],
|
653
|
+
["stat", ["FILENAME"]],
|
654
|
+
["symlink", ["FILENAME_O", "FILENAME_N"]],
|
655
|
+
["truncate", ["FILENAME", "length"]],
|
656
|
+
["utime", ["atime", "mtime", "*FILENAMES"]]]
|
657
|
+
|
658
|
+
def_builtin_commands(File, normal_delegation_file_methods)
|
659
|
+
alias_method :rm, :delete
|
660
|
+
|
661
|
+
# method related FileTest
|
662
|
+
def_builtin_commands(FileTest,
|
663
|
+
FileTest.singleton_methods(false).collect{|m| [m, ["FILENAME"]]})
|
664
|
+
|
665
|
+
end
|
666
|
+
|
667
|
+
end
|
668
|
+
end
|