This is the first part of a little story about my personal quest to find a
solution to my subcommand needs. If you think you might have such needs, if you
are not sure if you do, or if you are simply curious, please read on.
Where I work, we develop a product that is based on several subsystems, written
in various languages and with different degrees of autonomy. In order to
provide an homogeneous development environment, we use a chrooted sandbox that
tries to imitate the actual environment where out software will run. We also
have access to some actual hardware boxes for testing our full images or the
actual network communication between boxes, and so on.
This basically means that in my daily work, in order to test stuff, I have to
run a thousand commands, all of them written in different times, by different
people, without conventions, sometimes without documentation, sometimes even
written in Perl (I know!).
This was giving me headaches, so I started to write little command wrappers
grouped by topic. I wrote them as shell functions, since some of them would
require changing the current directory on the running shell. One of these
wrappers, for example one called foo, would look like this:
# Check if the given name can be executed as a command
[[ -z $1 ]] && return 1
type -t "$1" > /dev/null
# A light launcher for all things 'foo-'
if has_dep "$command" ; then
echo "Command 'foo $subcommand' not found"
has_dep foo-help && foo-help
With this simple structure, I can then have a collection of programs, scripts,
functions, aliases, whatever, as long as its name starts with foo-, that
can be launched with this foo wrapper/launcher. It will call foo-help
if it exists in cases where the entered subcommand does not exists.
For this to be usable for me at all, I had to set up shell completion for it.
This is how:
compgen -c foo- | sed 's/^foo-//' | grep "^$2.*"
complete -C __complete_foo -o default foo
This is more than enough for a simple need, but after I had created four or
five of those, with a number of subcommands each, my environment ended up being
polluted with lots of those subcommands in the PATH.
Also, the help command for each one needs to be maintained in sync with
available subcommands manually and, even worse, shell completion works only up
to the second token (the subcommand).
More problems appeared when I started playing a bit with the Fish shell,
but I found myself extremely unproductive without all my bash functions and
wrapper/launchers, so I started trying to port them. It was a pain, and it was
not worth it, since I might choose to change shells again the week after.
And so I started to look for some way to organise scripts in a hierarchy of sub
commands, with these requirements:
- Multiple levels of subcommands, so that I could put everything under a huge
master command called perhaps work.
- An easy way to keep documentation and inline help synchronised with the
changes in the commands.
- Some easy way to provide shell completion, all the way in.
- Ideally, that I could have the subcommands outside the PATH, to keep the
So, for a while, all I found was SM. It seems like a great project, and I
started trying to implement my needs with it, I got a lot of help from the
maintainers. BUT it was simply too much. I could not understand exactly how it
worked and at some point I got stuck getting the most basic bits to behave as
expected, so I temporarily gave up and continued using my simple wrappers and
trying to port them to Fish.
But hope was not lost, and, in fact, I found another tool that, while simple
enough to be understood in a short session of "just looking at the code", it
provided most of my requirements. But that, I’ll leave for part II.
There are comments.