The 60 second getopts tutorial
October 15th, 2007
Processing command line arguments is a pain in any language. If done manually, parsing even a few options and option value pairs in BASH is a huge pain. As such and given the nature of shell scripts, they usually have exceedingly poor options processing. However, there is a solution. getopts is a BASH builtin which makes handling command line arguments like butter.
Here is how I have done so in this monitor cpu usage script. First I define all my option holding variables:
whatTowatch="" email="" startAtUid="-1" maxCpuUsage="-1" debug=""
The following while statement loops through all the options and sets them to the corresponding variable. getopts returns true while there are options to be processed. The argument string, here “hw:e:u:m:d”, specifies which options the script accepts. If the user specifies an option which is not in this string, it sets $optionName to ?. If the option is succeeded by a colon, the value immediately following the option is placed in the variable $OPTARG.
while getopts "hw:e:u:m:d" optionName; do case "$optionName" in h) printHelpAndExit 0;; d) debug="0";; w) whatTowatch="$OPTARG";; e) email="$OPTARG";; u) startAtUid="$OPTARG";; m) maxCpuUsage="$OPTARG";; [?]) printErrorHelpAndExit "$badOptionHelp";; esac done
All that’s left to do, is to make sure you have the correct option groups specified and your off and running.
outputCmd="mail -s 'CPU Abusers on ${HOSTNAME}' $email"
[[ "$whatTowatch" != "users" ]] && [[ "$whatTowatch" != "procs" ]] \
&& printErrorHelpAndExit "$watchHelp"
if [[ -z "$debug" ]]
then
( [[ "$maxCpuUsage" -ge 0 ]] && [[ "$maxCpuUsage" -le 100 ]] ) || \
printErrorHelpAndExit "$maxCpuHelp"
[[ "$startAtUid" -eq -1 ]] && [[ "$whatTowatch" == "users" ]] && \
printErrorHelpAndExit "$uidHelp"
[[ -z "$email" ]] && printErrorHelpAndExit "$emailHelp"
else
outputCmd=cat
fi
Here is what the script outputs when it encounters an unknown option:
# ./monitorCpuUsage.sh -x ./monitorCpuUsage.sh: illegal option -- x Option not recognised
The “./monitorCpuUsage.sh: illegal option — x” portion is printed by getopts. If you want to remain silent, just place a colon at the front of the argument string.


October 31st, 2007 at 1:21 pm
A simple but urgent query — I will be very grateful if you advise please. I am writing a script
xyz.ksh. It can be run in two modes:-
1) without any parameters.
2) with a tablename as xyz.ksh -s table_name
I am using
while getopts s:s table_name
do
case $table_name in
s) TABLE_NAME=$$OPTARG;;
done
The problem I am having is if someone is running by mistake “xyz.ksh -s” that is with the parameter but without the value I am being unable to throw an exception.
Please can you advise.
October 31st, 2007 at 2:02 pm
I think what you want is:
TABLE_NAME=”"
while getopts "s:" table_namedo
case $table_name in
s) TABLE_NAME=$$OPTARG;;
done
if [[ -z "$TABLE_NAME" ]]
then
# whatever you want with no parms
fi
That way if they set -s without a param, getopts will complain.
February 20th, 2008 at 2:10 pm
Over the past couple days, I’ve been experimenting with getopts trying to get arguments to a bash shell script given via command line. I came to the conclusion that getopts must not be used from inside a bash function, but rather from the main body of the bash script itself. Does this match other people’s experience?
February 23rd, 2009 at 4:32 am
Thanks for this concise tutorial! It helped.
March 4th, 2009 at 4:20 pm
What is $badOptionHelp set to? It doesn’t seem to be automatically set like OPTARG.
For that matter, you seem to assume that function printHelpAndExit exists.
March 21st, 2009 at 1:48 pm
Responding to rubin427: getopts is an odd built-in function that implicitly processes “$@”, and “$@” inside a function means the arguments to the function rather the arguments to the script as a whole. Unless you pass “$@” to the function that processes getopts, you are unlikely to be successful.
This script works (both ksh and bash – the (a) notation in the case does not work in classic Bourne shell, but the rest does):
#!bin/ksh
aflag=0
bflag=0
ffile=”"
somefunc()
{
echo “somefunc: $*”
while getopts “abf:” x
do
case $x in
(a) aflag=1;;
(b) bflag=1;;
(f) ffile=”$OPTARG”;;
(*) echo “Dunno!” 1>&2; exit 1;;
esac
done
}
somefunc “$@”
echo “aflag=$aflag”
echo “bflag=$bflag”
echo “ffile=$ffile”
April 7th, 2009 at 1:53 pm
How does the optstring look for optional flags I need to achieve this:
script.sh -(e|d) -i ‘//’ -o // -p
I have resigned to the fact that the -i flag’s value, let’s say ‘/bin/*.sh’ will always have to be enclosed in single quotes, since bash will always try to expand it to a list of filenames if you don’t.
May 19th, 2009 at 12:06 pm
[...] command -o arg1 -p -q arg2 <arg for command> Options start with a – and may or may not have arguments, plus they can occur in any order. Hence getopts getopts tutorial – The 60 second getopts tutorial [...]
July 29th, 2009 at 2:44 am
rubin: Your conclusion is correct. What you can do, is collect $* and pass it to the function that you want to run getopts from. Otherwise getopts examines the arguements to the function that it is ran from. This can be usefull for allowing a functions syntax to mirror that of an external program.
August 26th, 2009 at 1:47 am
In response to rubin427:
That makes sense from both a general programming and bash perspective. The $# and associated variables are relative to scope.
You will return all the command line parameters when examining $# from the main body, with $0 being the executed script name as called.
When checking $# from within a function it will return the function arguments (if any) with the function name (=$0).
December 14th, 2009 at 11:54 am
“‘/bin/*.sh’ will always have to be enclosed in single quotes, since bash will always try to expand it to a list of filenames if you don’t.”
It will not be expanded inside any quotes, single or double.
“collect $* and pass it to the function that you want to run getopts from”
No, you pass “$@” (with the quotes).