Rs 0.1.2

= rs
=== Synopsis
rs is a project to implement a non-traditional object-oriented shell in

The three main features will be the simple power of Ruby as the shell
language, an objectlike interface to files and object piping.

=== Notes
Version 0.1.2.

This version has a rather more robust parser so
entering Ruby code on the command-line is more
natural. Some changes were also made to prompt
handling. For all changes, see doc/CHANGES.

Anyone who reads this entire post may award
themselves one (1) pat on the back. Sorry
about the length.

=== Authors
See doc/AUTHORS.

=== Licence
See doc/LICENCE.

=== Communication

=== Documentation

=== Features

  • Execution of arbitrary Ruby
  • Pipes, input redirection
  • FileSystemObjects which encapsulate paths and files

A development roadmap is in doc/TODO.roadmap

=== Download
==== Developers
Anyone interested in developing rs should read the doc/HOWTO.development
document, available on the web (see Documentation) or in the

==== Users

=== Installation
It is advised that developers do not install the program locally at
this point but rather use bin/rs directly. Users may use the provided
setup.rb script:

sudo ruby setup.rb all

=== Using rs
==== General notes
rs uses Readline which means that you can use the arrow
keys to go up and down in history and back and forth in
the current line.

==== Customising
rs will load ~/.rs/rc file as Ruby code if it exists.
You can use this file to execute any code you like each
time rs starts up.

==== Executing Ruby code
You should be able to execute any Ruby code on the line.
The parser is reasonably robust but not nearly perfect.
An expression continuing on another line can be indicated
with \ at the end of a line.

rs> 5 + 6
=> 11

rs> class Foo
…> def bar
…> puts ‘Baz’
…> end
…> end
=> nil
=> nil
rs> a = [1,
…> 2]
=> [1,2]

==== Output and environment control
You can affect the output using $config values of ruby_return,
prompt and continuation_prompt. $config (and $env) behave like
OpenStructs with the distinction that a method ending with ?
returns a boolean and one ending with ! will set the attribute
to true.

Prompts may be set as static Strings or anything that responds
to #call.

rs> 1 + 1
=> 2
rs> $config.ruby_return = false
rs> 1 + 1
rs> $config.ruby_return!
=> true

rs> $config.prompt
=> "rs> "
rs> $config.prompt = lambda {Dir.pwd + '> '}
=> <#Proc…>
/tmp> class Foo
…> end
/tmp> $config.continuation_prompt = $config.prompt
=> <#Proc…>
/tmp> ‘/home’
/home> ‘/tmp’
/tmp> class Foo
/tmp> end
/tmp> $config.prompt = "rs> "; $config.continuation_prompt = "…> "
=> "rs> "

rs accesses the system and internal envs through $env. Any uppercase
method name, such as $env.PATH, works on system envs. In contrast,
any lowercase such as $env.path will not access or be propagated to
the outside environment.

==== FileSystemObjects
FSOs give a relatively object-like interface to files and paths
and incorporate several File, FileUtils, Dir etc. methods.

rs> ‘/tmp’.to_fso.methods.sort
=> ["/", “<”, “<<”, “====”, “=====”, “=~”, “>”, “>>”, “id”,
“append_to”, “args”, “basename”, “blockdev?”, “cat”, “cd”,
“chmod”, “chmod_R”, “chown”, “chown_R”, “class”, “clone”, “compare”,
“cp”, “cp_r”, “directory?”, “dirname”, “display”, “dup”, “eql?”,
“exec”, “executable?”, “executable_real?”, “exist?”, “exists?”,
“extname”, “file?”, “find”, “freeze”, “frozen?”, “ftype”, “glob”,
“hash”, “id”, “inspect”, “install”, “instance_eval”, “instance_of?”,
“instance_variable_get”, “instance_variable_set”,
“is_a?”, “kind_of?”, “ln”, “ln_s”, “ln_sf”, “lstat”, “method”,
“mkdir”, “mkdir_p”, “mv”, “nil?”, “object_id”, “owned?”, “path”,
“pipe?”, “private_methods”, “protected_methods”, “public_methods”,
“readable_real?”, “readlink”, “relative_path”, “respond_to?”, “rm”,
“rm_rf”, “rmdir”, “run”, “send”, “setgid?”, “setuid?”,
“size”, “size?”, “socket?”, “split”, “stat”, “sticky?”, “symlink”,
“taint”, “tainted?”, “to_a”, “to_os”, “to_s”, “touch”, “truncate”,
“umask”, “unlink”, “untaint”, “writable?”, “writable_real?”,
“write_to”, “zero?”, “|”]

Generally, these methods behave exactly as their Ruby counterparts with
the path
of the FSO given as the file to operate on. For example:

rs> ‘/tmp’
=> true
rs> ‘/tmp/quux’.to_fso.exist?
=> false
rs> ‘/tmp’ {’./quux’.to_fso.touch}
=> nil
rs> ‘/tmp/quux’.to_fso.exist?
=> true
rs> ‘/tmp/quux’.to_fso.rm
=> [’/tmp/quux’]
rs> ‘/tmp/quux’.to_fso.exist?
=> false

You could of course put the FSO in a variable to avoid the
repetition–also, if
you feel like metaprogramming a bit, you could put a
String#method_missing in your
~/.rs/rc so that you can skip the #to_fso (which will eventually go
away, of course).

==== Executing programs
FSOs containing executable files may (unsurprisingly) be executed. One
thing to
know about the processing of FSOs is that currently any filename that
does not
start with ./, …/, / or ~/ is considered to be ‘unqualified’ and must
exist in
$PATH. In addition to this, unknown methods at the top-level are first
as unqualified files (falling back on normal if not found). The UI
special handling and will automatically run executables. Arguments may
also be

rs> ‘ls’

=> nil
rs> ls

=> nil
rs> ls ‘-la’

=> nil
rs> ‘ls’.to_fso.args(’-l’)

=> nil

==== Input redirection
More or less arbitrary objects can be ‘redirected’, > indicating
and >> appending. In both cases, the file will be created if it does not

rs> ‘/tmp/foo’.to_fso.touch
=> ["/tmp/foo"]
rs> ‘/tmp/foo’
=> nil
rs> ‘/tmp/foo’.to_fso < “Foo”
=> 3
rs> ‘/tmp/foo’
=> nil
rs> ‘/tmp/foo’.to_fso << 45
=> 2
rs> ‘/tmp/foo’
=> nil
rs> ‘/tmp/nonexist’.to_fso < ‘/tmp/foo’
=> 5
rs> ‘/tmp/nonexist’
=> nil

The opposite should also work:

rs> ‘/tmp/nonexist’
=> nil
rs> 78 >> ‘/tmp/nonexist’.to_fso
=> 2
rs> ‘/tmp/nonexist’
=> nil

There are a few exceptions. If the ‘input’ is an Array, it is
joined with newlines. If the input is an executable FSO, it will be run
and the result written as a String. Thirdly, if the file TO which the
is going is executable, it is converted to an ObjectStream instead. This
brings us to our next topic.

==== Pipes
Executable programs (and/or static input) can be chained together to
an arbitrary degree using ObjectStreams, also known as pipes.

The result of a piping operation can be queried with #result (this is
done automatically by the UI if the value of the expression is an OS).

Alternatively, an iterator interface is exposed with #each (an other
Enumerable methods).

A few modifications take place on a Ruby object being piped: Arrays are
newline-joined, #to_proc objects are #called and everything else is set
to its #to_s representation.

rs> ls | wc
8 8 46
=> nil
rs> (ls | wc).result
=> [" 8 8 46"]
rs> “foo\nbar” | wc(’-l’)
=> nil
rs> “foo\nbar” | wc(’-l’)
=> nil
rs> (ls | tr(‘a-z A-Z’)).each {|f| p f.reverse}
=> #IO:0x65be40
rs> lambda {“foo\nbar”} | wc
2 2 8
=> nil

=== Current issues and bugs
The entire project is in flux. Think of it as an exploratory programming
experiment that will stretch all the way until 0.9 at which point the
real program will be extracted from the codebase at the time.

  • Generally not production-ready.
  • Cannot be used as a login shell.
  • Inconsistent and incomplete internal model.
  • The ‘lexer’ for determining completeness of Ruby statements has been
    but is still somewhat weak.
  • Tests, though relatively extensive, mainly ensure that the correct
    is present. Work is needed in testing for failure.

=== Dependencies
==== Bundled

==== Unbundled

  • None

=== Special thanks

  • Everyone who contributed to the original ruSH code.
  • The Ruby hackers. I never appreciated the UNIXness of Ruby enough.

=== Copyright
Copyright © 2005-2006 by the Authors. All rights reserved.

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs