Class: Shell::CommandProcessor

Inherits:
Object
  • Object
show all
Defined in:
lib/shell/command-processor.rb

Overview

In order to execute a command on your OS, you need to define it as a Shell method.

Alternatively, you can execute any command via Shell::CommandProcessor#system even if it is not defined.

Constant Summary collapse

NoDelegateMethods =
m

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(shell) ⇒ CommandProcessor

Returns a new instance of CommandProcessor.



69
70
71
72
# File 'lib/shell/command-processor.rb', line 69

def initialize(shell)
  @shell = shell
  @system_commands = {}
end

Class Method Details

.add_delegate_command_to_shell(id) ⇒ Object

:nodoc:



572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
# File 'lib/shell/command-processor.rb', line 572

def self.add_delegate_command_to_shell(id) # :nodoc:
  id = id.intern if id.kind_of?(String)
  name = id.id2name
  if Shell.method_defined?(id)
    Shell.notify "warn: override definition of Shell##{name}."
    Shell.notify "warn: alias Shell##{name} to Shell##{name}_org.\n"
    Shell.module_eval "alias #{name}_org #{name}"
  end
  Shell.notify "method added: Shell##{name}.", Shell.debug?
  Shell.module_eval(%Q[def #{name}(*args, &block)
                        begin
                          @command_processor.__send__(:#{name}, *args, &block)
                        rescue Exception
                          [email protected]_if{|s| /:in `__getobj__'$/ =~ s} #`
                          [email protected]_if{|s| /^\\(eval\\):/ =~ s}
                        raise
                        end
                      end], __FILE__, __LINE__)

  if Shell::Filter.method_defined?(id)
    Shell.notify "warn: override definition of Shell::Filter##{name}."
    Shell.notify "warn: alias Shell##{name} to Shell::Filter##{name}_org."
    Filter.module_eval "alias #{name}_org #{name}"
  end
  Shell.notify "method added: Shell::Filter##{name}.", Shell.debug?
  Filter.module_eval(%Q[def #{name}(*args, &block)
                        begin
                          self | @shell.__send__(:#{name}, *args, &block)
                        rescue Exception
                          [email protected]_if{|s| /:in `__getobj__'$/ =~ s} #`
                          [email protected]_if{|s| /^\\(eval\\):/ =~ s}
                        raise
                        end
                      end], __FILE__, __LINE__)
end

.alias_command(ali, command, *opts) ⇒ Object

call-seq:

alias_command(alias, command, *options) ->  self

Creates a command alias at the given alias for the given command, passing any options along with it.

Shell::CommandProcessor.alias_command "lsC", "ls", "-CBF", "--show-control-chars"
Shell::CommandProcessor.alias_command("lsC", "ls"){|*opts| ["-CBF", "--show-control-chars", *opts]}


432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
# File 'lib/shell/command-processor.rb', line 432

def self.alias_command(ali, command, *opts)
  ali = ali.id2name if ali.kind_of?(Symbol)
  command = command.id2name if command.kind_of?(Symbol)
  begin
    if iterator?
      @alias_map[ali.intern] = proc

      eval((d = %Q[def #{ali}(*opts)
                      @shell.__send__(:#{command},
                                      *(CommandProcessor.alias_map[:#{ali}].call *opts))
                    end]), nil, __FILE__, __LINE__ - 1)

    else
       args = opts.collect{|opt| '"' + opt + '"'}.join(",")
       eval((d = %Q[def #{ali}(*opts)
                      @shell.__send__(:#{command}, #{args}, *opts)
                    end]), nil, __FILE__, __LINE__ - 1)
    end
  rescue SyntaxError
    Shell.notify "warn: Can't alias #{ali} command: #{command}."
    Shell.notify("Definition of #{ali}: ", d)
    raise
  end
  Shell.notify "Define #{ali} command: #{command}.", Shell.debug?
  Shell.notify("Definition of #{ali}: ", d,
         Shell.debug.kind_of?(Integer) && Shell.debug > 1)
  self
end

.alias_mapObject

Returns a list of aliased commands



420
421
422
# File 'lib/shell/command-processor.rb', line 420

def self.alias_map
  @alias_map
end

.def_builtin_commands(delegation_class, command_specs) ⇒ Object

:nodoc:

Delegates File and FileTest methods into Shell, including the following commands:

  • Shell#blockdev?(file)

  • Shell#chardev?(file)

  • Shell#directory?(file)

  • Shell#executable?(file)

  • Shell#executable_real?(file)

  • Shell#exist?(file)/Shell#exists?(file)

  • Shell#file?(file)

  • Shell#grpowned?(file)

  • Shell#owned?(file)

  • Shell#pipe?(file)

  • Shell#readable?(file)

  • Shell#readable_real?(file)

  • Shell#setgid?(file)

  • Shell#setuid?(file)

  • Shell#size(file)/Shell#size?(file)

  • Shell#socket?(file)

  • Shell#sticky?(file)

  • Shell#symlink?(file)

  • Shell#writable?(file)

  • Shell#writable_real?(file)

  • Shell#zero?(file)

  • Shell#syscopy(filename_from, filename_to)

  • Shell#copy(filename_from, filename_to)

  • Shell#move(filename_from, filename_to)

  • Shell#compare(filename_from, filename_to)

  • Shell#safe_unlink(*filenames)

  • Shell#makedirs(*filenames)

  • Shell#install(filename_from, filename_to, mode)

And also, there are some aliases for convenience:

  • Shell#cmp <- Shell#compare

  • Shell#mv <- Shell#move

  • Shell#cp <- Shell#copy

  • Shell#rm_f <- Shell#safe_unlink

  • Shell#mkpath <- Shell#makedirs



513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
# File 'lib/shell/command-processor.rb', line 513

def self.def_builtin_commands(delegation_class, command_specs)
  for meth, args in command_specs
    arg_str = args.collect{|arg| arg.downcase}.join(", ")
    call_arg_str = args.collect{
      |arg|
      case arg
      when /^(FILENAME.*)$/
        format("expand_path(%s)", $1.downcase)
      when /^(\*FILENAME.*)$/
        # \*FILENAME* -> filenames.collect{|fn| expand_path(fn)}.join(", ")
        $1.downcase + '.collect{|fn| expand_path(fn)}'
      else
        arg
      end
    }.join(", ")
    d = %Q[def #{meth}(#{arg_str})
                #{delegation_class}.#{meth}(#{call_arg_str})
             end]
    Shell.notify "Define #{meth}(#{arg_str})", Shell.debug?
    Shell.notify("Definition of #{meth}: ", d,
                 Shell.debug.kind_of?(Integer) && Shell.debug > 1)
    eval d
  end
end

.def_system_command(command, path = command) ⇒ Object

call-seq:

def_system_command(command, path)  ->  Shell::SystemCommand

Defines a command, registering path as a Shell method for the given command.

Shell::CommandProcessor.def_system_command "ls"
  #=> Defines ls.

Shell::CommandProcessor.def_system_command "sys_sort", "sort"
  #=> Defines sys_sort as sort


393
394
395
396
397
398
399
400
401
402
403
404
# File 'lib/shell/command-processor.rb', line 393

def self.def_system_command(command, path = command)
  begin
    eval((d = %Q[def #{command}(*opts)
              SystemCommand.new(@shell, '#{path}', *opts)
           end]), nil, __FILE__, __LINE__ - 1)
  rescue SyntaxError
    Shell.notify "warn: Can't define #{command} path: #{path}."
  end
  Shell.notify "Define #{command} path: #{path}.", Shell.debug?
  Shell.notify("Definition of #{command}: ", d,
         Shell.debug.kind_of?(Integer) && Shell.debug > 1)
end

.initializeObject



39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/shell/command-processor.rb', line 39

def self.initialize

  install_builtin_commands

  # define CommandProcessor#methods to Shell#methods and Filter#methods
  for m in CommandProcessor.instance_methods(false) - NoDelegateMethods
    add_delegate_command_to_shell(m)
  end

  def self.method_added(id)
    add_delegate_command_to_shell(id)
  end
end

.install_builtin_commandsObject

Delegates File methods into Shell, including the following commands:

  • Shell#atime(file)

  • Shell#basename(file, *opt)

  • Shell#chmod(mode, *files)

  • Shell#chown(owner, group, *file)

  • Shell#ctime(file)

  • Shell#delete(*file)

  • Shell#dirname(file)

  • Shell#ftype(file)

  • Shell#join(*file)

  • Shell#link(file_from, file_to)

  • Shell#lstat(file)

  • Shell#mtime(file)

  • Shell#readlink(file)

  • Shell#rename(file_from, file_to)

  • Shell#split(file)

  • Shell#stat(file)

  • Shell#symlink(file_from, file_to)

  • Shell#truncate(file, length)

  • Shell#utime(atime, mtime, *file)



630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
# File 'lib/shell/command-processor.rb', line 630

def self.install_builtin_commands
  # method related File.
  # (exclude open/foreach/unlink)
  normal_delegation_file_methods = [
    ["atime", ["FILENAME"]],
    ["basename", ["fn", "*opts"]],
    ["chmod", ["mode", "*FILENAMES"]],
    ["chown", ["owner", "group", "*FILENAME"]],
    ["ctime", ["FILENAMES"]],
    ["delete", ["*FILENAMES"]],
    ["dirname", ["FILENAME"]],
    ["ftype", ["FILENAME"]],
    ["join", ["*items"]],
    ["link", ["FILENAME_O", "FILENAME_N"]],
    ["lstat", ["FILENAME"]],
    ["mtime", ["FILENAME"]],
    ["readlink", ["FILENAME"]],
    ["rename", ["FILENAME_FROM", "FILENAME_TO"]],
    #      ["size", ["FILENAME"]],
    ["split", ["pathname"]],
    ["stat", ["FILENAME"]],
    ["symlink", ["FILENAME_O", "FILENAME_N"]],
    ["truncate", ["FILENAME", "length"]],
    ["utime", ["atime", "mtime", "*FILENAMES"]]]

  def_builtin_commands(File, normal_delegation_file_methods)
  alias_method :rm, :delete

  # method related FileTest
  def_builtin_commands(FileTest,
               FileTest.singleton_methods(false).collect{|m| [m, ["FILENAME"]]})

end

.install_system_commands(pre = "sys_") ⇒ Object

call-seq:

install_system_commands(prefix = "sys_")

Defines all commands in the Shell.default_system_path as Shell method, all with given prefix appended to their names.

Any invalid character names are converted to _, and errors are passed to Shell.notify.

Methods already defined are skipped.



548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
# File 'lib/shell/command-processor.rb', line 548

def self.install_system_commands(pre = "sys_")
  defined_meth = {}
  for m in Shell.methods
    defined_meth[m] = true
  end
  sh = Shell.new
  for path in Shell.default_system_path
    next unless sh.directory? path
    sh.cd path
    sh.foreach do
      |cn|
      if !defined_meth[pre + cn] && sh.file?(cn) && sh.executable?(cn)
        command = (pre + cn).gsub(/\W/, "_").sub(/^([0-9])/, '_\1')
        begin
          def_system_command(command, sh.expand_path(cn))
        rescue
          Shell.notify "warn: Can't define #{command} path: #{cn}"
        end
        defined_meth[command] = command
      end
    end
  end
end

.method_added(id) ⇒ Object



48
49
50
# File 'lib/shell/command-processor.rb', line 48

def self.method_added(id)
  add_delegate_command_to_shell(id)
end

.run_configObject

include run file.



56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/shell/command-processor.rb', line 56

def self.run_config
  begin
    load File.expand_path("~/.rb_shell") if ENV.key?("HOME")
  rescue LoadError, Errno::ENOENT
  rescue
    print "load error: #{rc}\n"
    print $!.class, ": ", $!, "\n"
    for err in $@[0, $@.size - 2]
      print "\t", err, "\n"
    end
  end
end

.unalias_command(ali) ⇒ Object

call-seq:

unalias_command(alias)  ->  self

Unaliases the given alias command.



465
466
467
468
469
# File 'lib/shell/command-processor.rb', line 465

def self.unalias_command(ali)
  ali = ali.id2name if ali.kind_of?(Symbol)
  @alias_map.delete ali.intern
  undef_system_command(ali)
end

.undef_system_command(command) ⇒ Object

call-seq:

undef_system_command(command) ->  self

Undefines a command



410
411
412
413
414
415
416
# File 'lib/shell/command-processor.rb', line 410

def self.undef_system_command(command)
  command = command.id2name if command.kind_of?(Symbol)
  remove_method(command)
  Shell.module_eval{remove_method(command)}
  Filter.module_eval{remove_method(command)}
  self
end

Instance Method Details

#append(to, filter) ⇒ Object



317
318
319
320
321
322
323
324
325
326
# File 'lib/shell/command-processor.rb', line 317

def append(to, filter)
  case to
  when String
    AppendFile.new(@shell, to, filter)
  when IO
    AppendIO.new(@shell, to, filter)
  else
    Shell.Fail Error::CantApplyMethod, "append", to.class
  end
end

#cat(*filenames) ⇒ Object

call-seq:

cat(*filename) ->  Cat

Returns a Cat object, for the given filenames



302
303
304
# File 'lib/shell/command-processor.rb', line 302

def cat(*filenames)
  Cat.new(@shell, *filenames)
end

#check_pointObject Also known as: finish_all_jobs

:nodoc:



261
262
263
# File 'lib/shell/command-processor.rb', line 261

def check_point # :nodoc:
  @shell.process_controller.wait_all_jobs_execution
end

#concat(*jobs) ⇒ Object

call-seq:

concat(*jobs) ->  Concat

Returns a Concat object, for the given jobs



340
341
342
# File 'lib/shell/command-processor.rb', line 340

def concat(*jobs)
  Concat.new(@shell, *jobs)
end

#echo(*strings) ⇒ Object

call-seq:

echo(*strings) ->  Echo

Returns a Echo object, for the given strings



294
295
296
# File 'lib/shell/command-processor.rb', line 294

def echo(*strings)
  Echo.new(@shell, *strings)
end

#expand_path(path) ⇒ Object

CommandProcessor#expand_path(path)

  path:   String
  return: String
returns the absolute path for <path>


80
81
82
# File 'lib/shell/command-processor.rb', line 80

def expand_path(path)
  @shell.expand_path(path)
end

#find_system_command(command) ⇒ Object

private functions



357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
# File 'lib/shell/command-processor.rb', line 357

def find_system_command(command)
  return command if /^\// =~ command
  case path = @system_commands[command]
  when String
    if exists?(path)
      return path
    else
      Shell.Fail Error::CommandNotFound, command
    end
  when false
    Shell.Fail Error::CommandNotFound, command
  end

  for p in @shell.system_path
    path = join(p, command)
    if FileTest.exist?(path)
      @system_commands[command] = path
      return path
    end
  end
  @system_commands[command] = false
  Shell.Fail Error::CommandNotFound, command
end

#foreach(path = nil, *rs) ⇒ Object

call-seq:

foreach(path, record_separator) -> Enumerator
foreach(path, record_separator) { block }

See IO.foreach when path is a file.

See Dir.foreach when path is a directory.



92
93
94
95
96
97
98
99
100
101
# File 'lib/shell/command-processor.rb', line 92

def foreach(path = nil, *rs)
  path = "." unless path
  path = expand_path(path)

  if File.directory?(path)
    Dir.foreach(path){|fn| yield fn}
  else
    IO.foreach(path, *rs){|l| yield l}
  end
end

#glob(pattern) ⇒ Object

def sort(*filenames)

  Sort.new(self, *filenames)
end

call-seq:

glob(pattern) ->  Glob

Returns a Glob filter object, with the given pattern object



313
314
315
# File 'lib/shell/command-processor.rb', line 313

def glob(pattern)
  Glob.new(@shell, pattern)
end

#mkdir(*path) ⇒ Object

call-seq:

mkdir(path)

Same as Dir.mkdir, except multiple directories are allowed.



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/shell/command-processor.rb', line 199

def mkdir(*path)
  @shell.check_point
  notify("mkdir #{path.join(' ')}")

  perm = nil
  if path.last.kind_of?(Integer)
    perm = path.pop
  end
  for dir in path
    d = expand_path(dir)
    if perm
      Dir.mkdir(d, perm)
    else
      Dir.mkdir(d)
    end
    File.chmod(d, 0666 & ~@shell.umask) if @shell.umask
  end
  Void.new(@shell)
end

#notify(*opts) ⇒ Object

%pwd, %cwd -> @pwd



345
346
347
348
349
350
351
352
# File 'lib/shell/command-processor.rb', line 345

def notify(*opts)
  Shell.notify(*opts) {|mes|
    yield mes if iterator?

    mes.gsub!("%pwd", "#{@cwd}")
    mes.gsub!("%cwd", "#{@cwd}")
  }
end

#open(path, mode = nil, perm = 0666, &b) ⇒ Object

call-seq:

open(path, mode, permissions) -> Enumerator
open(path, mode, permissions) { block }

See IO.open when path is a file.

See Dir.open when path is a directory.



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/shell/command-processor.rb', line 111

def open(path, mode = nil, perm = 0666, &b)
  path = expand_path(path)
  if File.directory?(path)
    Dir.open(path, &b)
  else
    if @shell.umask
      f = File.open(path, mode, perm)
      File.chmod(perm & ~@shell.umask, path)
      if block_given?
        f.each(&b)
      end
      f
    else
      f = File.open(path, mode, perm, &b)
    end
  end
end

#out(dev = STDOUT, &block) ⇒ Object

call-seq:

out(device) { block }

Calls device.print on the result passing the block to #transact



286
287
288
# File 'lib/shell/command-processor.rb', line 286

def out(dev = STDOUT, &block)
  dev.print transact(&block)
end

#rehashObject

call-seq:

rehash

Clears the command hash table.



257
258
259
# File 'lib/shell/command-processor.rb', line 257

def rehash
  @system_commands = {}
end

#rmdir(*path) ⇒ Object

call-seq:

rmdir(path)

Same as Dir.rmdir, except multiple directories are allowed.



223
224
225
226
227
228
229
230
231
# File 'lib/shell/command-processor.rb', line 223

def rmdir(*path)
  @shell.check_point
  notify("rmdir #{path.join(' ')}")

  for dir in path
    Dir.rmdir(expand_path(dir))
  end
  Void.new(@shell)
end

#system(command, *opts) ⇒ Object

call-seq:

system(command, *options) -> SystemCommand

Executes the given command with the options parameter.

Example:

print sh.system("ls", "-l")
sh.system("ls", "-l") | sh.head > STDOUT


242
243
244
245
246
247
248
249
250
251
# File 'lib/shell/command-processor.rb', line 242

def system(command, *opts)
  if opts.empty?
    if command =~ /\*|\?|\{|\}|\[|\]|<|>|\(|\)|~|&|\||\\|\$|;|'|`|"|\n/
      return SystemCommand.new(@shell, find_system_command("sh"), "-c", command)
    else
      command, *opts = command.split(/\s+/)
    end
  end
  SystemCommand.new(@shell, find_system_command(command), *opts)
end

#tee(file) ⇒ Object

call-seq:

tee(file) ->  Tee

Returns a Tee filter object, with the given file command



332
333
334
# File 'lib/shell/command-processor.rb', line 332

def tee(file)
  Tee.new(@shell, file)
end

#test(command, file1, file2 = nil) ⇒ Object Also known as: []

call-seq:

test(command, file1, file2) ->  true or false
[command, file1, file2] ->  true or false

Tests if the given command exists in file1, or optionally file2.

Example:

sh[?e, "foo"]
sh[:e, "foo"]
sh["e", "foo"]
sh[:exists?, "foo"]
sh["exists?", "foo"]


164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/shell/command-processor.rb', line 164

def test(command, file1, file2=nil)
  file1 = expand_path(file1)
  file2 = expand_path(file2) if file2
  command = command.id2name if command.kind_of?(Symbol)

  case command
  when Integer
    if file2
      top_level_test(command, file1, file2)
    else
      top_level_test(command, file1)
    end
  when String
    if command.size == 1
      if file2
        top_level_test(command, file1, file2)
      else
        top_level_test(command, file1)
      end
    else
      if file2
        FileTest.send(command, file1, file2)
      else
        FileTest.send(command, file1)
      end
    end
  end
end

#top_level_testObject

See Shell::CommandProcessor#test



150
# File 'lib/shell/command-processor.rb', line 150

alias top_level_test test

#transact(&block) ⇒ Object

call-seq:

transact { block }

Executes a block as self

Example:

sh.transact { system("ls", "-l") | head > STDOUT }


273
274
275
276
277
278
279
# File 'lib/shell/command-processor.rb', line 273

def transact(&block)
  begin
    @shell.instance_eval(&block)
  ensure
    check_point
  end
end

call-seq:

unlink(path)

See IO.unlink when path is a file.

See Dir.unlink when path is a directory.



137
138
139
140
141
142
143
144
145
146
147
# File 'lib/shell/command-processor.rb', line 137

def unlink(path)
  @shell.check_point

  path = expand_path(path)
  if File.directory?(path)
    Dir.unlink(path)
  else
    IO.unlink(path)
  end
  Void.new(@shell)
end