Build a Custom CLI with Bash

Brot & Games
3 min readApr 13, 2019


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).

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 

Brot and Games CLI
Version: 0.1.0

Usage: bagcli [command]

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 deployCommand: deployUsage: 
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.

├── 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>.
  • commands/deploy has its own help and usage instructions. It takes the parameter <project_name> and calls helm with parameters.
  • common 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.


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.

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.