Table of Content

This is a multiple-parts article. There are few sections here.

Overview
Getting Token
BASH CLI
BASH Script
BASH Option Argument
BASH Group Tools
Makefile
Manual Page

Bashful Bot

Goal: Utilize Command Line Argument Parser

Preface

Would it be nice if we can have help usage for our script ?

% cd ~/Documents/cupubot/bash

% ./main.bash --version
cupubot v0.001

% ./main.bash --help
usage:  cupubot [options]

operations:
 general
   -h, --help       display help information
   -v, --version    display version information

BASH: Telegram Bot: Script with Usage

Previous Guidance

We have already see a modular bash script in previous article. Now, let’s make this script useful for anyone by showing usage and version.

Issue

Although this looks easy, actually it is tricky. We need to parse argument from script, which is somehow, can be so complicated.

Luckily bash has, this OPTIN feature, comes to the rescue.


Additional Script

I’m going to make it as brief as possible. We need a bit of change in our main script. Two more scripts: messages.bash, and options.bash.

#!/usr/bin/env bash

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

# module: main
. ${DIR}/config.bash
. ${DIR}/messages.bash
. ${DIR}/options.bash
. ${DIR}/controller.bash

# module: task
. ${DIR}/tasks/observe.bash
. ${DIR}/tasks/reply.bash

### -- main --

get_options_from_arguments "$@"

Source:

Message Script

These are the functions, that will be called later. Politically, every public bash project should have these two. Do not let your user, getting confused in the dark without a clue.

function message_usage() {
    cat <<-EOF
usage:  cupubot [options]

operations:
 general
   -h, --help       display help information
   -v, --version    display version information

EOF
}

function message_version() {
    local version='v0.001'
    echo "cupubot $version"
}

Source:

Beginner Version

Consider this short script.

#!/usr/bin/env bash

function get_options_from_arguments() {   
    # ! : indirect expansion
    while [[ -n "${!OPTIND}" ]]; do
        case "${!OPTIND}" in
            version)   
                message_version
                exit;;
        esac

        shift $OPTIND
        OPTIND=1
    done
}

You can run with this command:

% ./main.bash version
cupubot v0.001

How does it works

The OPTIND is an index of argument, the ! is indirect expansion. It means !OPTIND return the value of index.

When we pass one argument, the ${OPTIND} will have the value of index 1. And then the ${!OPTIND} have the value version. After shift the ${OPTIND} will have the value of index 0, and the loop will be terminated.

Option Script

Now the complete version of the script that process the argument.

# reading
# http://wiki.bash-hackers.org/howto/getopts_tutorial

# handling command
function handle_command_plain() {
	local command=$1

    case "$command" in
        version)   
            message_version
            exit;;
    esac
}

# handling -command
function handle_command_opt() {
	local command=$1
	
    case "$command" in
        -)
            handle_command_optarg "$OPTARG"
            ;;
         h)  
            message_usage
            exit;;
         v)  
            message_version
            exit;;
         *)  
            # Invalid Option
            message_usage
            exit;;
    esac
}

# handling --command
function handle_command_optarg() {
	local command=$1
	
    case "$command" in
        version) 
            message_version
            exit;;
        help) 
            message_usage
            exit;;
        observe)
            do_observe
            exit;;
        reply)
            loop_reply
            exit;;
        *) 
            # Invalid Option
            message_usage
            exit;;
    esac
}

function get_options_from_arguments() {  
	# get argument length	
	[[ $# -eq "0" ]] && message_usage && exit;
	 
    # ! : indirect expansion
    while [[ -n "${!OPTIND}" ]]; do
        handle_command_plain "${!OPTIND}"

        while getopts "vh-:" OPT; do
             handle_command_opt "$OPT"
        done

        shift $OPTIND
        OPTIND=1
    done
    
	# Invalid Option
    message_usage
    exit
}

Source:


How does it works

Most built-in BASH internal variable are not easy to understand. But once you get it, you will know the beauty.

Consider this chunck of code.

OPTIND

    while [[ -n "${!OPTIND}" ]]; do
        ...
        shift $OPTIND
        OPTIND=1
    done

The OPTIND is an index of argument, the ! is indirect expansion. It means !OPTIND return the value of index.

The while along with shift, will iterate argument from beginning to end.

In this case it will only use

  • version, or

  • use getopt "vh-:" instead

Hints:

% man optind

I must admit I cannot explain this concpet, but there is a good explanation here in stackoverflow.

getopts

        while getopts "vh-:" OPT; do
            case "$OPT" in
            ...
            esac
        done

What is this "vh-:" meaning ? It means getopt except these arguments

  • -v

  • -h

  • --:

The prefix : means, an argument expecting an options. Noe here are our options

  • -v

  • -h

  • --version

  • --help

Hints:

% man getopt

This is also a good reading.


Conclusion

BASH: Telegram Bot: Script with Usage

So here is our acceptable argument.

  • ./main-modular.bash

  • ./main-modular.bash version

  • ./main-modular.bash --version

  • ./main-modular.bash --help

  • ./main-modular.bash -v

  • ./main-modular.bash -h

Yeah, that is all. Not so complicated after all.

Can you read the pattern ?

What is Next ?

We will add some chat group tools such as

  • Greet New Member.

  • Text Logger.

  • HTML Logger.

Just have a look at the next article.

Thank you for reading.