The spinshell is a minimal shell that just raises the event Run. All
commands are defined as event handlers for
SpinShell.Run. All commands are
defined either in shell/Commands or in an extension.
The shell supports variable substitution and some rudimentary syntax
for simplifying your life. It also defines a few initial shell
variables based on the build configuration that you can use to help
simplify your life.
Commands
The shell directory is split into several component subdirectories,
each of which exports a separate interface and domain. There's no
fundamental reason for this other than to provide a simple (and used)
example of how to partition a bunch of code.
The different packages are:
- shellcore
- Basic parsing services and Shell.i3 interface.
- consoleshell
- Driver started up at boottime to attach the shell services to the console.
- basiccmds
- A collection of useful primitive commands. Most of what you need goes here.
- utils
- some additional "utility" programs that are not big enough to stand on their own elsewhere so they get thrown into the shell.
- expr
- Commands that treat other shell commands as expressions.
- regress
- Regression tests.
Using the Shell
To use the shell, type at it. The shell is a statically linked system
extension. That is, no kernel services rely on it, but it's hard to
get much done without it.
Shell commands are expected to adhere to a UNIX-like invocation syntax:
command -flag1 -flag2 argument -flag3 argument
Specifically, flags are prefixed with a "-" and arguments are just
given as they are.
IMPORTANT: Not all shell commands are so adhering. We'll fix this
over the next few releases.
Shell Variables
The shell allows you to set and access shell variables.
Useful commands provided here are:
- set var value
- sets the variable var to the value value.
- "set" by itself just dumps all the shell variables.
- $var
- expands var to the last set value.
- Variable
expansion is done before commands are passed through to their
handlers. So, you can even create aliases for commands that you don't
like to type.
- echo $var
- shows the value of $var.
Variable expansion works
Variable expansion is not as powerful in real shells, but it does work ok.
Supports $x is "foo", then
- $x expands to foo
- $x.bar expands to foo.bar (any non-alpha character terminates a shell variable)
- bar$x expands to barfoo
- $x$x expands to foofoo
Some things you can't do. For example, if you have $x and $xy, then
there is no way to refer to $x in the string $xy (eg, you can't
currently do ${x}y.) If this is annoying enough, we can fix it.
Some builtin shell variables
On system boot, the shell defines a few builtin shell values
that are of general use. If you don't like their values, you are
free to change them with "set."
- $user
- sets to the name of the person who built the system. eg "bershad".
- $home
- set to the root of the source tree from which the kernel was built,
eg, "/spin/bershad."
- This is a funny kind of root, since it is assumed to be valid in
the TFTP file namespace, which is a bit wierd. Basically, the root of
our tftp space is /afs/cs/project, so if your home is /spin/camcam,
that would correspond to /afs/cs/project/spin/camcam
- ~ (that's right, TILDE)
- equivalent to $home, eg, ~/spin/spindle/version/version.config
would name the config file
/afs/cs/project/spin/bershad/spin/spindle/version/version.config.
- $version
- expands to the version string embedded in the kernel. Useful for
figuring out what version of the system you are looking at.
- $builddir
- expands to where the kernel thinks it got stored during
build. For example, my $builddir usually expands to
/afs/cs/project/spin/bershad/spin/sal/SPIN. Useful for making sure
you're looking at the kernel you think you're looking at.
Builtin, or nearly builtin shell commands
Minimally, the shell supports only a few builtin operations:
- load -conf -trust file
- Load the named file as an extension. If -conf is specified, the
file is assumed to be a configuration file that contains object files
which should be loaded and linked into a single domain. If -trust is
specified, the file is loaded into a trusted domain.
- script -i file
- source the named file as though the commands had been typed in
from the shell. The special "-i" flag can be used without a filename
to read in the script /spin/$user/spin/scripts/$user.spininit, where
$user is whoever built the kernel.
- script -b
- runs /spin/$user/scpin/scripts/boot.rc
- help
- Lists all shell commands.
Shell commands that are extensions
Most shell commands are dynamically linked into the system once the
system boots. Type "help" from the spinshell prompt to discover what
these are.
Writing new Shell Commands
It's so very easy to write a new shell command. You need only three things:
- A command name.
- A help string.
- An entry function that implements your new command.
The basic idea is to create an interface for your command, define the
command name and help string in the interface, and also define the
entry function. Shazam. You feed this interface (and its
implementation) to a template (command.tmpl from the
sys/src/shell/Commands) directory and everything else is taken care of
for you.
Here's an example which defines the kernel's "date" command:
- src/Date.i3
- The interface
- src/Date.m3
- The implementation
- src/m3makefile
- The m3makefile.
It works like magic. When you build this stuff, you'll get four files that
must be downloaded to implement your command:
- DateCmd.io, DateCmd.mo
- The interface and implementation files for the generic instantiation of
your command.
- Date.io, Date.mo
- Your interface and implementation files.
If you are building a config file, you should specify the order as
DateCmd.io, DateCmd.mo, Date.io, Date.mo.
How does it work?
Your command module is providing an implementation for the generic
Command module. Three files are involved:
- Command.mg.
- Generic implementation.
- Command.ig.
- Generic interface.
- command.tmpl.
- The quake directives that instantiate your generic from the
CommandModule directive.
Generics make it all easy
This generic takes your "Run" command and installs it as a handler on
the SpinShell's exported Run event. It also creates and installs a
"Help" handler for the SpinShell.