Building Command-line Tools: Bash Edition

Building Command-line Tools: Bash Edition

by   Bryan Riley  |  December 3, 2018  |  0

Bash is my go-to language for building quick command-line tools, but it is also amazing at building extendable tools for wrapping software. A command-line tool with fully documented tags can make your software/code more user-friendly, and that gets your repo cloned. It’s no coincidence that Bash was one of the languages I listed in my Top 5 Languages to learn for 2019. Check it out when you get a chance!

The Situation


So let’s start off with something common: the “finished” piece of software (by finished, I mean software that works). This can be software of any category, but for this exercise let’s assume it’s a web service that can run locally on a computer. We want to give users a few options:

  • User should be able to check if they have all the necessary software to run the service (Maven, Gradle, Node, etc.)
  • User should be able to choose the port the service should run on
  • User should be able to see a help menu

 

That’s a good amount of features for now, but let’s see how a requirement looks as a raw command:

User should be able to check if they have all necessary software to run the service (Maven, Gradle, Node, etc)

command mvn -v || echo "Maven is not installed" &&
command node -v || echo "Node is not installed" &&
command gradle -v || echo "Gradle is not installed"


That’s a lot of commands just to get a rundown of what exists. Expecting users to write this out makes your code less appealing. To show how much we can simplify this, we’ll be updating this repo with our command line tool.

The Solution


This is a simple spring boot service, so our wrapper is also going to be pretty simple. Let’s start with a simple shell file; we’ll call it “funky.sh”. From our user requirements, we only have one requirement that needs user input: “User should be able to choose the port the service should run on.”

We’ll have a standard variable to handle that input:

PORT=
view raw
funky.sh
hosted with ❤ by GitHub

 

Next, we will loop over the arguments passed, put them into a variable “$1”, then case switch based on value:

while test $# -gt 0; do
    case $1 in
        -port)
            shift
            PORT=$1
            shift
            ;;
        -check)
                command mvn -v || echo Maven is not installed &&
                command java -version || echo Java is not installed
                exit 0;
                ;;
            -help)
                echo usage -port port_number
                echo tags:
                echo -port The port number you want the service to run on
                echo -check Checks if user has all necessary software to run service
                exit 0;
                ;;
            *)
                echo Command not supported
                exit 1;
                ;;
    esac
done
view raw
funky.sh
hosted with ❤ by GitHub

 

Let’s go over what’s happening here:

  • while test: – allows execution of code over a period where a condition evaluates to true
  • $ #: This is a variable that holds the number of arguments passed to the script
  • $# -gt 0: Our condition which “while test” checks. This condition translated means “while the count of arguments is greater than 0, evaluate to true”
  • while test $# -gt 0; : This is our loop which is used to run our inner “do” block.
  • case “$1” in: Simple switch case, that will match the variable “$1” to one of the execution blocks, -port), -check), -help). If it matches to none of these, it evaluates at the “)” block.
  • shift: Moves the reference of $1, to the next variable and lowers the value of $#.


For the tags of -help, -check, and default case, we exit because the script doesn’t need to run the service. For our -port case, we do need to execute some code on a passed variable. So let’s first do some logic checks before we run the script:

if [[ -z $PORT ]]; then
    echo Port number was not supplied, running on default port 8080
    eval mvn spring-boot:run
else
    eval mvn -Dserver.port=”$PORT” spring-boot:run
fi


With this code, we are using the
-z and [[ ]]  to check if the variable is still empty. Earlier in our switch case, we assign it a value if the -port tag is available; however, that doesn’t guarantee the variable isn’t empty. If it is, we will just run it on a default port.

And that’s all there is to it. There are plenty of ways to make this script better—like adding a check to port ensuring its a number—but that is just an addition to logic. The template for making a command line tool in Bash pretty simple, and I hope this helps you in your future projects. Below is the gist of what the script looks like and the Github repo, in which you can try the script out:

PORT=
while test $# -gt 0; do
    case $1 in
        -port)
            shift
            PORT=$1
            shift
            ;;
        -check)
            command mvn -v || echo Maven is not installed &&
            command java -version || echo Java is not installed
            exit 0;
            ;;
        -help)
            echo usage -port port_number
            echo tags:
            echo -port The port number you want the service to run on
            echo -check Checks if user has all necessary software to run service
            exit 0;
            ;;
        *)
            echo Command not supported
            exit 1;
            ;;
    esac
done
if [[ -z $PORT ]]; then
    echo Port number was not supplied, running on default port 8080
    eval mvn spring-boot:run
else
    eval mvn -Dserver.port=”$PORT” spring-boot:run
fi
view raw
funky.sh
hosted with ❤ by GitHub

 

The link to the Github repo can be found here.

Thank you so much for reading, and continue to build that cache!

 

Sign up for InRhythm updates: IR events, tech news, and more!

Leave a Reply




check out our new interactive scrum framework