GRAPES

"We strive to provide Good, Reliable, Advanced, Professional, Enriching & Safe (GRAPES) experience by our products & services"

Perl like GetOpt functionality for Shell Scripting

Perl like GetOpt functionality for Shell Scripting

One of the powerful features of the terminal app which’s available by default on Linux & Mac OS is the unix shells it supports.  It offers plethora of unix commands which can be bunched together in a file (called ‘the script’) to run as a group. You would use the script for example in-order to accomplish a task automatically and obviously this is one of the key features which everyone admires.  Ever wondered how to output a professional looking help when someone runs your script?  Something that’s available & supported in most of the GNU unix commands like sed?

Output of “sed –help” on a terminal

A good script would handle the arguments & options provided to it in an efficient way and outputs clear help/usage information.  One of the commands which the unix shells support is “GetOpt” which’s a in-built command, which can be used to parse arguments & options provided to it.  However, it’s not as user-friendly and feature rich as GetOpts function supported by programming languages like Perl, which can not only parse the options & arguments but also can store the values of some of those options in variables with intuitive names.  

As an example, let’s say you are developing a bash shell-script ‘sample.sh’ which you want it to support 3 options:

  • –help or -h: For displaying the help message
  • –version or -v: For displaying the version information
  • –file or -f: For taking the file name as one of the arguments 

Ever wondered, how easy it would be, if I can get a template which can:

  • Set the variable “opt_help” if either the long option “–help” or short option “-h” is given to the script
  • Set the variable “opt_version” if either the long option “–version” or short option “-v” is given to the script
  • Set the variable “opt_file” to have the file name as the value stored in it
  • Give error message if the user uses the “–file” or “-f” option without an associated file name as the argument
  • Give error message if any other option other than the 3 listed above are used

It would make the life of scripting so easy and take away the burden of handling all of these so that you can stay focussed on programming the main task.  This is what I have tried to accomplish in the template file below.  The ‘main’ function is the one you would use to code the main tasks while the other functions implement what I talked above.  You might want to modify the ‘Help’ function to output the appropriate usage information.  You might also want to edit the ‘GetOpts’ call on line #191 below to include additional options in the format ‘f|file:\’ at line #194 and so on.  The colon ‘:’ after the name of the long option tells the ‘GetOpts’ function that this particular option needs an argument.

Linux:

On Linux OS, the template should work as is without any issues.  As the GNU command line toolset which ships with the Linux is one of the best with lots of features.

Mac OS:

On Mac, you need to get the following installed before you could run this template

  • First install the Homebrew command.  There are plenty of blog posts available on the internet to guide you on how to install home-brew package on Mac
  • Once home brew is installed then you need to execute the following commands:
    • brew install gnu-getopt
    • brew install gnu-sed
    • brew install grep
  • The above 3 commands would install the GNU version of getopt, sed & grep commands which are needed for this template to work properly
  • Lastly, you need to edit your .cshrc file (see here for the blog on cshrc file) to edit the PATH variable as shown below
    •  setenv PATH /usr/local/opt/grep/libexec/gnubin:/usr/local/opt/gnu-sed/libexec/gnubin:/usr/local/opt/gnu-getopt/bin:${PATH}
  • Now you should be all set to use this template on a Mac

Outputs:

I have named this template as sample.sh to run it on the shell.

Output with “–help” or “-h” options

Output with “–version” or “-v” option

 

Output when a wrong option (say ‘-a’) is given

sample.sh

#!/bin/bash

#************************************************************#
## 					$_PGR.sh 				    	         #
## 				written by Amjad khan			             #
##			Email: khan.amjadull@gmail.com		             #
##			Phone: +91-9900497591				 	         #
##			                                	 	         #
## #
## #
#************************************************************#

##************ Define some functions ******##

##----------------- Version? --------------##
## Give the version information and exit   ##
## Command line options -v or --version	   ##
## Exit status is 0						   ##
##-----------------------------------------##
function version()
{
	VER=$(echo '$Revision$' | sed -e 's/.*: //; s/ .*//; s/\$Revision\$/0.0/')
	YEAR=$(date +%Y)
	
	cat <<- ___VERSION_INFO___
	$MY_PRG ###utility### $VER
	Written by Amjad khan

	Copyright (C) $YEAR - GRAPES PVT LTD
	All rights reserved.
	___VERSION_INFO___

	exit 0
}


##----------------- Help ----------------##
## Give the help information and exit	 ##
## Command line options -h or --help	 ##
## Exit status is 0						 ##
##---------------------------------------##
function Help()
 {
	cat <<- ___HELP_MESSAGE___
	Usage: $MY_PRG [OPTION]... [FILE]...


	Examples: 
	  $MY_PRG			# Comments

	## ---<description>---

	Mandatory arguments to long options are mandatory for short options too.

	## ---<explanation for args>---
  
  
	  -h, --help	display this help and exit
	  -v, --version	output version information and exit

	## ---<extra info>---

	Report bugs to <khan.amjadull@gmail.com>.
	___HELP_MESSAGE___

	exit 0
}


##----------------- Cleanup ----------------##
## Clean up temporary files					##
## Exit status is 0							##
##------------------------------------------##
function cleanup_tmpfiles {
	rm -rf $_TMP
}



##--------- Syntax error :( ---------##
## Help to try Help					 ##
##-----------------------------------##
function try_help {		# Direct the user to get some help		
	echo "Try \`$MY_PRG --help' for more information" && exit 1		
}																	


##---------------- Getopts ------------------##
## Utility very similar to perl getopts      ##
## Can handle long options and set variables ##
##-------------------------------------------##
function Getopts(){

	declare  shorts
	declare  longs
	declare  same
	declare -a args
	local index

	until [ "$1" = "--" ] ; do

		if echo $1 |grep -q -P '^\S\|\S+:$'; then
			shorts=(${shorts[@]} ${1%|*}:)
			longs=(${longs[@]} ${1#*|})
			same=(${same[@]} $1);
	
		elif echo $1 |grep -q -P '^\S\|\S+$'; then
			shorts=(${shorts[@]} ${1%|*})
			longs=(${longs[@]} ${1#*|})
			same=(${same[@]} $1);
	
		elif echo $1 |grep -q -P '^\S+\|\S:$'; then
			longs=(${longs[@]} ${1%|*}:)
			shorts=(${shorts[@]} ${1#*|})
			same=(${same[@]} $1);
	
		elif echo $1 |grep -q -P '^\S+\|\S$'; then
			longs=(${longs[@]} ${1%|*})
			shortss=(${shorts[@]} ${1#*|})
			same=(${same[@]} $1);

		elif echo $1 |grep -q -P '^\S:?$';then
			shorts=(${shorts[@]} $1);
	
		elif echo $1 |grep -q -P '^\S\S+:?$'; then
			longs=(${longs[@]} $1);
	
		fi
		shift;
	done

	shift;

	shorts=${shorts[@]}			

	longs=${longs[@]}

	regex=${longs// /\|}
	regex=${regex//:/\\S*}

	same=${same[@]//:/}

	for option in "$@";do
		if echo $option|grep -q -P '^--';then

			echo $option|grep -q -P "^--($regex)\b"

			[ "$?" = "1" ] && echo "$MY_PRG: unrecognized option '$option'" && return 1;
		fi	
	done;

	regex=$(echo ${same}|sed -e 's: :/;s/\\<:g;s:|:\\>/:g;s:^:\\<:')
	
	args=($(getopt -l ${longs// /,} -o ${shorts// /,} -n $MY_PRG -s bash -- $@))
	
	[ "$?" = 1 ] && return 1;

	let index=0;

	while [ "${args[$index]}" != "--" ];do

		if [ ! -z "${args[$index]%%\'*}" ];then

	    	option=$(echo opt${args[$index]}|sed -e "s/$regex/;s/-\+/_/g")
	    	eval $option=1;
		else
			eval $option=${args[$index]//\'/}
	    fi

		let index+=1
	done	    
}


##---------------- main --------------------##

##-------------------------------------------##
function main(){
true;
}
#*********************************************#

## Some script wide variables
MY_PRG=`basename $0`
_DATE=`date +%y%m%d%H%M`

## Some global variables
declare _TMP;	## Holds temporary files for cleanup

## Valid command line options
Getopts \
	'h|help'\
	'v|version'\
	-- $@

[ "$?" = "1" ] && try_help;

[ ! -z $opt_help ] && Help;

[ ! -z $opt_version ] && version;

time main;
exit;
###################################################
## $Log$
###################################################


Please feel free to reach out with any questions or comment. You can use the Contact Us to write to us.

Avatar photo

Amjad

Website: https://blog.al-zakirah202.familyds.com

I'm Amjad from Bangalore in India. I got a Bachelor's degree in Electronics & Communications from National Institute of Engineering in 2004. I currently work as Product & Test Engineer at a Semiconductor Company. I am currently married with 3 kids - fortunate to be blessed with a good wife as well! I enjoy programming and automating things wherever possible. I have presented many papers and delivered talks in technical conferences. Among my hobbies are setting up home servers for media, photo, books and others, language learning, electronic circuit analysis, software development and many more..

Leave a Reply

Your email address will not be published. Required fields are marked *