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.

11 Responses to “The 60 second getopts tutorial”

  1. sujoy Says:

    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.

  2. admin Says:

    I think what you want is:

    TABLE_NAME=”"
    while getopts "s:" table_name
    do
    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.

  3. rubin427 Says:

    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?

  4. Aaditya Bhatia Says:

    Thanks for this concise tutorial! It helped.

  5. Ken Says:

    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.

  6. Jonathan Lefflre Says:

    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”

  7. hyperion Says:

    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.

  8. passing arguments - Shell Programming and Scripting - The UNIX and Linux Forums Says:

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

  9. pierce.jason Says:

    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.

  10. intech_guy Says:

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

  11. Chris F.A. Johnson Says:

    “‘/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).

Leave a Reply

If Wordpress eats your comment (shell output, loops, ex..) email the text to me.