Class: Shell

Inherits:
Object
  • Object
show all
Extended by:
Exception2MessageMapper, Forwardable
Includes:
Error
Defined in:
lib/shell.rb

Overview

Shell implements an idiomatic Ruby interface for common UNIX shell commands.

It provides users the ability to execute commands with filters and pipes, like sh/csh by using native facilities of Ruby.

Examples

Temp file creation

In this example we will create three tmpFile‘s in three different folders under the /tmp directory.

sh = Shell.cd("/tmp") # Change to the /tmp directory
sh.mkdir "shell-test-1" unless sh.exists?("shell-test-1")
# make the 'shell-test-1' directory if it doesn't already exist
sh.cd("shell-test-1") # Change to the /tmp/shell-test-1 directory
for dir in ["dir1", "dir3", "dir5"]
  if !sh.exists?(dir)
    sh.mkdir dir # make dir if it doesn't already exist
    sh.cd(dir) do
      # change to the `dir` directory

f = sh.open(“tmpFile”, “w”) # open a new file in write mode f.print “TESTn” # write to the file f.close # close the file handler

    end
    print sh.pwd                  # output the process working directory
  end
end

Temp file creation with self

This example is identical to the first, except we’re using CommandProcessor#transact.

CommandProcessor#transact executes the given block against self, in this case sh; our Shell object. Within the block we can substitute sh.cd to cd, because the scope within the block uses sh already.

sh = Shell.cd("/tmp")
sh.transact do
  mkdir "shell-test-1" unless exists?("shell-test-1")
  cd("shell-test-1")
  for dir in ["dir1", "dir3", "dir5"]
    if !exists?(dir)

mkdir dir cd(dir) do f = open(“tmpFile”, “w”) f.print “TESTn” f.close end print pwd

    end
  end
end

Pipe /etc/printcap into a file

In this example we will read the operating system file /etc/printcap, generated by cupsd, and then output it to a new file relative to the pwd of sh.

sh = Shell.new
sh.cat("/etc/printcap") | sh.tee("tee1") > "tee2"
(sh.cat < "/etc/printcap") | sh.tee("tee11") > "tee12"
sh.cat("/etc/printcap") | sh.tee("tee1") >> "tee2"
(sh.cat < "/etc/printcap") | sh.tee("tee11") >> "tee12"

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(pwd = Dir.pwd, umask = nil) ⇒ Shell

call-seq:

Shell.new(pwd, umask) -> obj

Creates a Shell object which current directory is set to the process current directory, unless otherwise specified by the pwd argument.



183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/shell.rb', line 183

def initialize(pwd = Dir.pwd, umask = nil)
  @cwd = File.expand_path(pwd)
  @dir_stack = []
  @umask = umask

  @system_path = Shell.default_system_path
  @record_separator = Shell.default_record_separator

  @command_processor = CommandProcessor.new(self)
  @process_controller = ProcessController.new(self)

  @verbose = Shell.verbose
  @debug = Shell.debug
end

Class Attribute Details

.cascadeObject

Returns the value of attribute cascade



108
109
110
# File 'lib/shell.rb', line 108

def cascade
  @cascade
end

.debugObject Also known as: debug?

Returns the value of attribute debug



108
109
110
# File 'lib/shell.rb', line 108

def debug
  @debug
end

.verboseObject Also known as: verbose?

Returns the value of attribute verbose



108
109
110
# File 'lib/shell.rb', line 108

def verbose
  @verbose
end

Instance Attribute Details

#command_processorObject (readonly)

Returns the value of attribute command_processor



223
224
225
# File 'lib/shell.rb', line 223

def command_processor
  @command_processor
end

#cwdObject (readonly) Also known as: dir, getwd, pwd

Returns the current working directory.



243
244
245
# File 'lib/shell.rb', line 243

def cwd
  @cwd
end

#debugObject Also known as: debug?

Returns the value of attribute debug



213
214
215
# File 'lib/shell.rb', line 213

def debug
  @debug
end

#dir_stackObject (readonly) Also known as: dirs

Returns the value of attribute dir_stack



248
249
250
# File 'lib/shell.rb', line 248

def dir_stack
  @dir_stack
end

#process_controllerObject (readonly)

Returns the value of attribute process_controller



224
225
226
# File 'lib/shell.rb', line 224

def process_controller
  @process_controller
end

#record_separatorObject

Returns the value of attribute record_separator



212
213
214
# File 'lib/shell.rb', line 212

def record_separator
  @record_separator
end

#system_pathObject

Returns the command search path in an array



199
200
201
# File 'lib/shell.rb', line 199

def system_path
  @system_path
end

#umaskObject

Returns the umask



211
212
213
# File 'lib/shell.rb', line 211

def umask
  @umask
end

#verboseObject Also known as: verbose?

Returns the value of attribute verbose



213
214
215
# File 'lib/shell.rb', line 213

def verbose
  @verbose
end

Class Method Details

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

call-seq:

alias_command(alias, command, *opts, &block)

Convenience method for Shell::CommandProcessor.alias_command. Defines an instance method which will execute a command under an alternative name.

Shell.def_system_command('date')
Shell.alias_command('date_in_utc', 'date', '-u')
Shell.new.date_in_utc # => Sat Jan 25 16:59:57 UTC 2014


392
393
394
# File 'lib/shell.rb', line 392

def Shell.alias_command(ali, command, *opts, &block)
  CommandProcessor.alias_command(ali, command, *opts, &block)
end

.cd(path) ⇒ Object

call-seq:

Shell.cd(path)

Creates a new Shell instance with the current working directory set to path.



125
126
127
# File 'lib/shell.rb', line 125

def cd(path)
  new(path)
end

.def_system_command(command, path = command) ⇒ Object

call-seq:

def_system_command(command, path = command)

Convenience method for Shell::CommandProcessor.def_system_command. Defines an instance method which will execute the given shell command. If the executable is not in Shell.default_system_path, you must supply the path to it.

Shell.def_system_command('hostname')
Shell.new.hostname # => localhost

# How to use an executable that's not in the default path

Shell.def_system_command('run_my_program', "~/hello")
Shell.new.run_my_program # prints "Hello from a C program!"


372
373
374
# File 'lib/shell.rb', line 372

def Shell.def_system_command(command, path = command)
  CommandProcessor.def_system_command(command, path)
end

.default_record_separatorObject



158
159
160
161
162
163
164
# File 'lib/shell.rb', line 158

def default_record_separator
  if @default_record_separator
    @default_record_separator
  else
    $/
  end
end

.default_record_separator=(rs) ⇒ Object



166
167
168
# File 'lib/shell.rb', line 166

def default_record_separator=(rs)
  @default_record_separator = rs
end

.default_system_pathObject

Returns the directories in the current shell’s PATH environment variable as an array of directory names. This sets the system_path for all instances of Shell.

Example: If in your current shell, you did:

$ echo $PATH
/usr/bin:/bin:/usr/local/bin

Running this method in the above shell would then return:

["/usr/bin", "/bin", "/usr/local/bin"]


142
143
144
145
146
147
148
# File 'lib/shell.rb', line 142

def default_system_path
  if @default_system_path
    @default_system_path
  else
    ENV["PATH"].split(":")
  end
end

.default_system_path=(path) ⇒ Object

Sets the system_path that new instances of Shell should have as their initial system_path.

path should be an array of directory name strings.



154
155
156
# File 'lib/shell.rb', line 154

def default_system_path=(path)
  @default_system_path = path
end

.install_system_commands(pre = "sys_") ⇒ Object

call-seq:

install_system_commands(pre = "sys_")

Convenience method for Shell::CommandProcessor.install_system_commands. Defines instance methods representing all the executable files found in Shell.default_system_path, with the given prefix prepended to their names.

Shell.install_system_commands
Shell.new.sys_echo("hello") # => hello


412
413
414
# File 'lib/shell.rb', line 412

def Shell.install_system_commands(pre = "sys_")
  CommandProcessor.install_system_commands(pre)
end

.notify(*opts) ⇒ Object



425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
# File 'lib/shell.rb', line 425

def self.notify(*opts)
  Shell::debug_output_synchronize do
    if opts[-1].kind_of?(String)
      yorn = verbose?
    else
      yorn = opts.pop
    end
    return unless yorn

    if @debug_display_thread_id
      if @debug_display_process_id
        prefix = "shell(##{Process.pid}:#{Thread.current.to_s.sub("Thread", "Th")}): "
      else
        prefix = "shell(#{Thread.current.to_s.sub("Thread", "Th")}): "
      end
    else
      prefix = "shell: "
    end
    _head = true
    STDERR.print opts.collect{|mes|
      mes = mes.dup
      yield mes if iterator?
      if _head
        _head = false
        prefix + mes
      else
        " "* prefix.size + mes
      end
    }.join("\n")+"\n"
  end
end

.unalias_command(ali) ⇒ Object

Convenience method for Shell::CommandProcessor.unalias_command



397
398
399
# File 'lib/shell.rb', line 397

def Shell.unalias_command(ali)
  CommandProcessor.unalias_command(ali)
end

.undef_system_command(command) ⇒ Object

Convenience method for Shell::CommandProcessor.undef_system_command



377
378
379
# File 'lib/shell.rb', line 377

def Shell.undef_system_command(command)
  CommandProcessor.undef_system_command(command)
end

Instance Method Details

#chdir(path = nil, verbose = @verbose) ⇒ Object Also known as: cd

call-seq:

Shell.chdir(path)

Creates a Shell object which current directory is set to path.

If a block is given, it restores the current directory when the block ends.

If called as iterator, it restores the current directory when the block ends.



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/shell.rb', line 260

def chdir(path = nil, verbose = @verbose)
  check_point

  if iterator?
    notify("chdir(with block) #{path}") if verbose
    cwd_old = @cwd
    begin
      chdir(path, nil)
      yield
    ensure
      chdir(cwd_old, nil)
    end
  else
    notify("chdir #{path}") if verbose
    path = "~" unless path
    @cwd = expand_path(path)
    notify "current dir: #{@cwd}"
    rehash
    Void.new(self)
  end
end

#expand_path(path) ⇒ Object



226
227
228
# File 'lib/shell.rb', line 226

def expand_path(path)
  File.expand_path(path, @cwd)
end

#inspectObject



417
418
419
420
421
422
423
# File 'lib/shell.rb', line 417

def inspect
  if debug.kind_of?(Integer) && debug > 2
    super
  else
    to_s
  end
end

#jobsObject

Returns a list of scheduled jobs.



344
345
346
# File 'lib/shell.rb', line 344

def jobs
  @process_controller.jobs
end

#kill(sig, command) ⇒ Object

call-seq:

kill(signal, job)

Sends the given signal to the given job



352
353
354
# File 'lib/shell.rb', line 352

def kill(sig, command)
  @process_controller.kill_job(sig, command)
end

#popdirObject Also known as: popd

Pops a directory from the directory stack, and sets the current directory to it.



328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/shell.rb', line 328

def popdir
  check_point

  notify("popdir")
  if pop = @dir_stack.pop
    chdir pop
    notify "dir stack: [#{@dir_stack.join ', '}]"
    self
  else
    Shell.Fail DirStackEmpty
  end
  Void.new(self)
end

#pushdir(path = nil, verbose = @verbose) ⇒ Object Also known as: pushd

call-seq:

pushdir(path)
pushdir(path) { &block }

Pushes the current directory to the directory stack, changing the current directory to path.

If path is omitted, it exchanges its current directory and the top of its directory stack.

If a block is given, it restores the current directory when the block ends.



294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/shell.rb', line 294

def pushdir(path = nil, verbose = @verbose)
  check_point

  if iterator?
    notify("pushdir(with block) #{path}") if verbose
    pushdir(path, nil)
    begin
      yield
    ensure
      popdir
    end
  elsif path
    notify("pushdir #{path}") if verbose
    @dir_stack.push @cwd
    chdir(path, nil)
    notify "dir stack: [#{@dir_stack.join ', '}]"
    self
  else
    notify("pushdir") if verbose
    if pop = @dir_stack.pop
      @dir_stack.push @cwd
      chdir pop
      notify "dir stack: [#{@dir_stack.join ', '}]"
      self
    else
      Shell.Fail DirStackEmpty
    end
  end
  Void.new(self)
end