Gosh: a pluggable command shell in Go

Since the plugin system was announced, I wanted to created a framework for creating interactive command shell programs where commands are implemented using Go plugins (the idea originated from an old Java project of mine). So I created Gosh (Go shell).

Gosh — https://github.com/vladimirvivien/gosh

Gosh (Go shell) uses a driver shell program to load the command plugins at runtime. The plugins implement handlers which respond to command-line input at the prompt. When a user types a command, the driver dispatches the plugin registered to handle the command.

Checkout my blog post on Writing Modular Go Programs with Plugins.

Gosh Design

When the Gosh shell program starts, it loads Go plugins (*_command.so) files from a specified directory. Each plugin is registered with the name of the commands it handles. After command registration, the program displays the gosh> prompt, ready for commands.

Gosh process

When a user types a command at the prompt, the Gosh driver process matches the name of the command with the registered symbol capable of handling the typed command. New commands can be added to customize Gosh without recompiling the shell binary.

Gosh command plugins implement the Command interface which uses method Exec to specify the behavior of the command.

type Command interface {
Name() string
Usage() string
ShortDesc() string
LongDesc() string
Exec(context.Context, []string) (context.Context, error)
}

A command plugin is expected to export a variable symbol of type Commands which provides a registry of available Command implemented.

type Commands interface {
...
Registry() map[string]Command
}
See Gosh/plugins/testcmd.go for an example of Gosh command plugin.

How it works

Before you take Gosh for a spin, be aware of Go plugin pre-requisites:

  • Go version 1.8
  • Linux OS (for now)

Download or clone the Gosh repository (see link above). For a quick start, run the following:

go run shell/gosh.go

You will get the following output indicating that there are no commands as expected:

                        888
888
888
.d88b. .d88b. .d8888b 88888b.
d88P"88bd88""88b88K 888 "88b
888 888888 888"Y8888b.888 888
Y88b 888Y88..88P X88888 888
"Y88888 "Y88P" 88888P'888 888
888
Y8b d88P
"Y88P"

No commands found

Next, exit the gosh shell (Ctrl-C) and compile the example plugin that comes with the source code.

go build -buildmode=plugin   \
-o plugins/sys_command.so \
plugins/syscmd.go

The previous command will compile plugins/syscmd.go and outputs shared object plugins/sys_command.so, as a Go plugin file. That plugin implements several system commands including help , to display help messages, and exit which exists the shell REPL.

You can verify that the previous build command created the shared object file:

> ls -lh plugins/
total 3.2M
-rw-rw-r-- 1 4.5K Mar 19 18:23 syscmd.go
-rw-rw-r-- 1 3.2M Mar 19 19:14 sys_command.so
-rw-rw-r-- 1 1.4K Mar 19 18:23 testcmd.go

Now, when gosh is restarted, it will dynamically load the commands it finds in the ./plugins directory:

> go run shell/gosh.go
...
Loaded 4 command(s)...
Type help for available commands
gosh>

As indicated in the output above, typing help lists all available commands in the shell:

gosh> help
help: prints help information for other commands.
Available commands
------------------
prompt: sets a new shell prompt
sys: sets a new shell prompt
help: prints help information for other commands.
exit: exits the interactive shell immediately
Use "help <command-name>" for detail about the specified command

Next, we can type command sys to display system information and exit when done.

gosh> sys
System Info
-----------
arc: amd64
os: linux
cpus: 4
mem: 1740800
hostname: ubuntu
pagesize: 4096
groupid: 1000
userid: 1000
pid: 31483
exec: /tmp/go-build118078117/command-line-arguments/_obj/exe/gosh
gosh>  exit
exiting...

If you are interested in implementing your own Gosh command plugin, checkout gosh/plugins/testcmd.go. It is used for testing, but implements all methods needed for a working command plugin.

Conclusion

Gosh demonstrates what can be done with the new Go plugin system. The program provides a platform for building powerful interactive command shells using a scalable and pluggable design. The hope is that you find Gosh useful and create your own command shell system on top of it. Further, I hope that you use this as a model to create scalable modular programs in Go!

As always, if you find this writeup useful, please let me know by clicking on the icon to recommend this post.

Also, don’t forget to checkout my book on Go, titled Learning Go Programming from Packt Publishing.