Shell scripting libraries

To really take advantage of automating tasks using shell scripts, it's important to organize all common tasks into reusable commands and have them available in the path. To do this, it's a good idea to create a bin folder inside the home directory for the scripts, and a bin/lib directory for storing common pieces of code. When working with lots of shell scripts, it's important to reuse large pieces of functionality. This can be achieved by writing library functions for your shell scripts, functions that you can call from multiple places.

Here we will create a library script called util.sh, which will be sourced in other scripts. By sourcing the script, we get access to functions and variables from inside the library script.

We will start by adding the print_ip function from a previous script.

Now we will add another function called getarg, which will be used by other scripts for reading command line arguments and values. We will simply paste it from our clipboard history, using ClipIt to select it.

You can learn more about ClipIt by checking out our ClipIt section!

Function to read cli argument:
function getarg() {
    NAME=${1}
    while [[ ! -z ${2} ]]; do
        if [[ "--${NAME}" == "${2}" ]]; then
            echo "${3}"
            break
        fi
        shift
    done
}   
Shell scripting libraries

This is just a simple function that will receive a parameter name as the first argument, the list of CLI arguments as the second parameter, and it will search inside the list of CLI arguments to find the parameter name. We will see it in action later on.

The last function we're going to create is called get_public_ip. It is similar in terms of functionality to the print_ip function, except that it will be used to print the computer's public IP. That means that, if you are connected to a wireless router and you access the Internet, you will get the IP of the router, which is the IP that other sites see. The print_ip function just shows the IP address from the private subnet.

The command is already copied in the clipboard. It's called dig and we're using it to access https://www.opendns.com/ in order to read the public ip. You can find more information about it in its man page or by Googling it:

function get_public_ip() {
    dig +short myip.opendns.com @resolver1.opendns.com
}

Now that we have our library functions in place, let's go and create our productivity booster scripts. Let's create a script called iputils where we will add some common tasks for reading IP addresses.

We'll start by adding the shebang, followed by a neat little trick for making sure we are always in the same folder as the executed script. We will be using the BASH_SOURCE variable to determine the value of the current working directory (or CWD) variable. You see here that we are using nested subshells in order to achieve this:

CWD=$( cd "$(dirname "${BASH_SOURCE[0]}" )/" && pwd )
cd ${CWD}

Next, we will source the util script, so that the library functions are exported into memory. Then, we can access them from the current script:

source ${CWD}/lib/util.sh

Let's add a simple call to our getarg function using a subshell, and search for the cmd argument. Also, let's echo what we've found, so that we can test our script:

CMD=$(getarg cmd $@)
echo ${CMD}

The next thing we need to do is to give the script execution rights using the chmod command. Also, in order to run the script from anywhere, the bin folder must be in the PATH variable. Echo the variable and check that the bin folder is there and, if not, update the variable in ~/.zshrc.

Let's test the script by reading a command line parameter with the getarg function and echoing it.

If you are searching for the iputils command in the terminal using tab for autocomplete and the command doesn't seem to exist, that is probably because you need to tell zsh to reload its path commands. To do this, issue the "rehash" command.

Now run:

iputil --cmd ip

This should work from within any folder, and it should print ip on the screen.

Now that we've verified everything is alright, let's write some code for our command line arguments. If we run the script with the --cmd ip flags, the script should print that on the screen. This can be done with the already-familiar case statement. Here we also want to pass in another argument, --iface, to get the interface that's needed for printing the IP. It's also a good practice to add a default case and echo a message saying invalid argument:

case ${CMD} in
    ip)
        IFACE=$(getarg iface $@)
        print_ip ${IFACE}
        ;;
    publicip)
        get_public_ip
        ;;
    *)
        echo "Invalid argument"
esac

Save the script, and let's test it.

First, let's get the interface name from the ifconfig command, and then let's go and test the script by running this command:

iputil --cmd ip --iface wlp3s0
Shell scripting libraries

We can see it's printing our private ip on the screen.

Now let's add our last cmd to the script: publicip.

For this we just call the get_public_ip function from our lib utility. Save it and run this:

iputil --cmd publicip

We see that the command worked; our public ip is printed on the screen. Here is the complete script:

#!/bin/bash 

CWD=$( cd "$( dirname "${BASH_SOURCE[0]}" )/" && pwd )
cd ${CWD}

source ${CWD}/lib.sh

CMD=$(getarg cmd $@)

case ${CMD} in
    publicip)
        print_public_ip
        ;;
    ip)
        IFACE=$(getarg iface $@)
        print_ip $IFACE
        ;;
    *)
        echo "invalid command"
esac

To give you an example, a while ago there were a bunch of articles on the Internet about a man who used to automate everything that took him more than 90 seconds to do. The scripts he wrote included instructing the coffee machine to start making a latte, so that by the time he got to the machine, the latte was finished and he didn't need to wait. He also wrote a script that sent a text message "late at work" to his wife and automatically picked a reason from a preset list whenever there was activity with his login on the company's servers after 9 p.m.

Of course, this example is a little bit complex, but in the end it's all about your imagination. Well-written automation scripts can take care of your routine work and leave you to explore your creative potential.