Bash is my go to 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. Bash was one of the languages I listed in my video Top 5 Languages to learn 2019 video, check it out when you get a chance.
The Situation
So lets start off with something common, the “finished” piece of software. By finished, I mean it works. This can be software of any category but let’s assume it’s a webservice that can be ran locally on a computer. However we want to give users a few options:
- User should be able to check if they have all 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 lets 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 is a lot of commands just to get a run down of what exist or not. Expecting users to write this out makes your code less appealing. The repo we will be updating with our command line tool is here
This is a simple spring boot service, so our wrapper is going to be pretty simple. Lets start with a simple shell file, let’s call it “funky.sh”. From our user requirements, we only have one requirement that requires user input, “User should be able to choose the port the service should run on”.
So lets have a standard variable to handle that input
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| PORT='' |
Next we will loop over the arguments passed, put them into a variable “$1”, then case switch based on value.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |
Lets go over what’s happening here:
- while test: – allows to execute code over a period where a condition evaluates to true
- $# : This is a variable that holds the amount 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 our 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 lets first do some logic checks before we run the script:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 [[ ]] as 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 empty, we will just run it on a default port.
And that’s all there is to it. Now 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 is pretty simple and I hope this helps you in your future projects. Below is the complete gist of what the script looks like and the github repo in which you can try the script out on.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | |
Github Repo located here
Thank you so much for reading and continue to build that cache.
