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