Written on September 1, 2018
It has been a little over a year since I left Windows altogether and started using Linux on all of my machines. In that time, I have learnt many uses of the Linux console, and forgotten just as many helpful commands and arguments. As I continued to use the Linux terminal, I quickly found myself browsing stack exchange and scouring man-pages 3-4 times a day, taking up a noticeable amount of my development time. Originally, as seen in one of the early commits to my dotfiles repository, I started to write a “help” file containing some of the more common commands that I could grep to quickly find the command/syntax for what I wanted to do, but I found myself wanting a little more functionality from it. Typing out “cat ~/help” and “grep drive mount” every time I wanted to mount a drive seemed unnecessary, and I began to want the ability to “stack” certain commands to avoid accidentally grep-ing lines from the wrong category (a note about “connecting to network drives” under “drive” would be unnecessarily returned when I was trying to connect to a network, for example).
To solve this, well, what else could I have done but start a new project? Nothing could stop me now. I created another repository to add to my existing 70-some open-source projects without even flinching. As for my choice of language, this time I decided to go with Go rather than sticking with NodeJS which I used to make my previous small console programs. I had seen Go used for a couple other projects that I liked and was getting curious about it, so this was a good opportunity to try it out.
I found a very nice, well written article detailing how to set up a go development environment and start writing a basic program with it. Granted, it is two years old, but its instructions still hold up fairly well, as do its points about the benefits of using Go over another language. One thing I especially liked about it was the comparitively small amount of dependencies that Go uses, which is a weak point of NodeJS. It is also nice that when installing a dependency, Go actually downloads its source code directly to its “environment” (a folder that all of the Go projects are stored in) and builds it from source with the program that you write, making it fairly easy to eliminate any vulnerabilities from the software that is built with it. If that is considered a problem for this use case I do not know, but the sense of security is still very nice to have.
The program that I ended up writing was appropriately dubbed “AAH”, or the “Annoyingly Advanced Helper”. Its source of information is two YAML-formatted configuration files: one stored in the “.config” directory that is automatically downloaded from the GitHub repository when the program is first run, and another stored directly in the home directory that can be created and edited by the user. In the program, the values from these two files are merged together, with the file in the home dir taking precedence. The file from the GitHub repository only contains information about general commands that work across most Linux distributions and don’t change much based on user preference, while the file that the user can create in their home dir is intended for commands specific to them and the Linux distribution that they use.
When AAH is run with arguments, it reads the files for corresponding sets of nested keys. For example, if the arguments provided were “something stuff yes”, it would first look for the key “something”, then look inside it for the key “stuff”, and finally output the value of “yes”. If “yes” doesn’t exist, it will output the entire tree under “something stuff” - the same would happen if “stuff” and/or “something” were not present in either of the files. However, it isn’t always necessary to type out the entire key for a value. If “something” is the only key that starts with “some”, then passing “some stuff yes” would have the exact same result as typing out the whole key. This makes it significantly faster to find common information such as commands under the “network” key; instead of typing out “AAH network connect”, all that is necessary is “AAH net con”.
On a side note, I also used a similar method of matching the start of the string when implementing the standard “–help” and “–version” arguments. As usual, “-h” and “–help” work, and so do “-v” and “–version”. However, “–v” and “-version” are also accepted without error, as is “-ver” and pretty much anything in-between. This isn’t exactly a design flaw since there is no way that one of these arguments could be confused with a key in one of the YAML files because of the starting “-“, but I would certainly like to see someone’s reaction if they were to mistype “–hel” expecting an error, then get the same output.
Overall, considering that I have only spent a little over a day on this project, I am happy with the result. It fulfills its purpose nicely, and is quite good at doing it. You can find the project’s source code and installation instructions here.
Also, shout out to Ashish Shekar for spotting my mistake building the binaries to publish on GitHub, which was creating a SIGSEGV when the program tried to download a copy of the YAML file from GitHub.