123456789_123456789_123456789_123456789_123456789_

Class: Shell::CommandProcessor

Relationships & Source Files
Inherits: Object
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 #system even if it is not defined.

Constant Summary

Class Method Summary

Instance Method Summary

Constructor Details

.new(shell) ⇒ CommandProcessor

[ GitHub ]

  
# 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)

This method is for internal use only.
[ GitHub ]

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

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
                          $@.delete_if{|s| /:in `__getobj__'$/ =~ s} #`
                          $@.delete_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
                          $@.delete_if{|s| /:in `__getobj__'$/ =~ s} #`
                          $@.delete_if{|s| /^\\(eval\\):/ =~ s}
                        raise
                        end
                      end], __FILE__, __LINE__)
end

.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]}
[ GitHub ]

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

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_map

Returns a list of aliased commands

[ GitHub ]

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

def self.alias_map
  @alias_map
end

.def_builtin_commands(delegation_class, command_specs)

This method is for internal use only.

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

[ GitHub ]

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

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) ⇒ 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
[ GitHub ]

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

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

.initialize

[ GitHub ]

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

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_commands

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)

[ GitHub ]

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

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"]],
    ["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(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.

[ GitHub ]

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

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)

[ GitHub ]

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

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

.run_config

include run file.

[ GitHub ]

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

def self.run_config
  rc = "~/.rb_shell"
  begin
    load File.expand_path(rc) 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(alias) ⇒ self

Unaliases the given alias command.

[ GitHub ]

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

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) ⇒ self

Undefines a command

[ GitHub ]

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

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

#[](command, file1, file2 = nil)

Alias for #test.

[ GitHub ]

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

alias [] test

#append(to, filter)

[ GitHub ]

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

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(*filename) ⇒ Cat

Returns a Cat object, for the given filenames

[ GitHub ]

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

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

#check_point Also known as: #finish_all_jobs

This method is for internal use only.
[ GitHub ]

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

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

#concat(*jobs) ⇒ Concat

Returns a Concat object, for the given Shell#jobs

[ GitHub ]

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

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

#echo(*strings) ⇒ Echo

Returns a Echo object, for the given strings

[ GitHub ]

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

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

#expand_path(path)

#expand_path(path)

  path:   String
  return: String
returns the absolute path for <path>
[ GitHub ]

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

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

#find_system_command(command)

private functions

[ GitHub ]

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

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)
    begin
      st = File.stat(path)
    rescue SystemCallError
      next
    else
      next unless st.executable? and !st.directory?
      @system_commands[command] = path
      return path
    end
  end
  @system_commands[command] = false
  Shell.Fail Error::CommandNotFound, command
end

#finish_all_jobs

This method is for internal use only.

Alias for #check_point.

[ GitHub ]

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

alias finish_all_jobs check_point # :nodoc:

#foreach(path, record_separator) ⇒ Enumerator #foreach(path, record_separator)

See IO.foreach when path is a file.

See Dir.foreach when path is a directory.

[ GitHub ]

  
# 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) ⇒ Glob

def sort(*filenames)

  Sort.new(self, *filenames)
end

Returns a Glob filter object, with the given pattern object

[ GitHub ]

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

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

#mkdir(path)

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

[ GitHub ]

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

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)

%pwd, %cwd -> @pwd

[ GitHub ]

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

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

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

#open(path, mode, permissions) ⇒ Enumerator #open(path, mode, permissions)

See IO.open when path is a file.

See Dir.open when path is a directory.

[ GitHub ]

  
# 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
      File.open(path, mode, perm, &b)
    end
  end
end

#out(device)

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

[ GitHub ]

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

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

#rehash

Clears the command hash table.

[ GitHub ]

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

def rehash
  @system_commands = {}
end

#rmdir(path)

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

[ GitHub ]

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

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, *options) ⇒ SystemCommand

Executes the given command with the options parameter.

Example:

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

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

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) ⇒ Tee

Returns a Tee filter object, with the given file command

[ GitHub ]

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

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

#test(command, file1, file2) ⇒ Boolean #[](command, file1, file2) ⇒ Boolean
Also known as: #[]

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"]
[ GitHub ]

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

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
      unless FileTest.methods(false).include?(command.to_sym)
        raise "unsupported command: #{ command }"
      end
      if file2
        FileTest.send(command, file1, file2)
      else
        FileTest.send(command, file1)
      end
    end
  end
end

#top_level_test

See #test

[ GitHub ]

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

alias top_level_test test

#transact

Executes a block as self

Example:

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

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

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