When we say “CLI” we’re talking about a program that we can run at the command line by simply typing the command name and its parameters.

CLI example

When modern languages like Ruby, Python or Go are not available or not allowed due to compliance rules, Bash scripting is the way to go if you want to build a custom CLI and be sure that it runs on a variety of systems.

Bash ( bash ) is one of many available (yet the most commonly used) Unix shells. Bash stands for "Bourne Again SHell", and is a replacement/improvement of the original Bourne shell ( sh ).

Source: https://askubuntu.com/a/172487

Here we are building a modular CLI with a configuration file and (sub-) commands including help and usage instructions using Bash compliant scripts.

A name to command them all via parameters

First we choose a name and think about the commands and its parameters. The CLI in this example should deploy a series of applications to a Kubernetes Cluster via Helm in the end. Your use case could be something different ;)

We call the CLI bagcli which stands for Brot and Games CLI and have a final view of the command line in our minds — this should be the output of calling bagcli without a command or when a command is misspelled.

$ ./bagcli



bagcli

Brot and Games CLI

Version: 0.1.0



Usage: bagcli [command]



Commands:

deploy Deploy

* Help

The command bagcli deploy should have its own usage instructions which should also be shown if the parameter <project_name> is missing.

$ ./bagcli deploy Command: deploy Usage:

deploy project_name

Modular directory structure

Next we decide upon the directory structure for our CLI. Since we are building a modular CLI with one file for each (sub-) command, we need a clean directory structure.

/path/to/bagcli/

├── VERSION

├── bagcli

├── commands

│ └── deploy

├── common

└── config.template

The resulting Bash scripts and files in detail

bagcli is the entrypoint script which loads the config and decides upon the command/parameters which file to execute from the commands directory.

Example: bagcli deploy <project_name> processes the command and the parameter and calls commands/deploy with parameter <project_name> .

is the entrypoint script which loads the config and decides upon the command/parameters which file to execute from the directory. Example: processes the command and the parameter and calls with parameter . commands/deploy has its own help and usage instructions. It takes the parameter <project_name> and calls helm with parameters.

has its own help and usage instructions. It takes the parameter and calls with parameters. common holds all the functions which all the scripts need (eg. for logging).

holds all the functions which all the scripts need (eg. for logging). config.template is like the name says a template for easy configuration of the CLI by the end-user. Users are instructed to cp config.template config and update the file according to their needs. The file config should be ignored by version systems like Git due to possible sensitive data.

Following is a basic implementation of the core files: bagcli , commands/deploy , common and config.template .

Conclusion

The final result is an extensible CLI bagcli with currently one command bagcli deploy implemented.

It can be easily extended by cp commands/deploy commands/my_command and editing the commands/my_command file to fit your needs. Don’t forget to hook it up in the case statement in the bagcli file (entrypoint for all commands).

There is also a GitHub repository where you can find an up-to-date version with better error handling and other improvements.

https://github.com/brotandgames/bagcli

You can use it as a blueprint or skeleton for your projects.

This article has been submitted to Hacker News and reddit on the 4th of May 2019.

Thanks for reading.

💋 Keep it simple and stupid.