dannyman.toldme.com


Technical

Argument Parsing in Shell Scripts

So, say you are writing a shell script (sh or bash) that needs to take arguments like so:

./script.sh start
./script.sh -v start
./script.sh -c foo.conf start
./script.sh -vc foo.conf start

This took me a bit of doing. First, I tried getopt but apparently all that does is disambiguate -v from -c. That, and the FreeBSD man page claims to have improved the example by making it completely inscrutable.

Eventually I found a reference that explained how to use getopts, which worked a lot better, but all the examples I could find didn’t tell you how to reset $@.

Eventually, I figured out that that last bit is accomplished with shift.

Here’s example code:

#!/bin/sh
#

conf=default.conf
while getopts c:hv o
do case "$o" in
    c)  conf="$OPTARG";;
    h)  echo "Usage: $0 -hv -c <config> [start|stop|restart|status]" && exit 1;;
    v)  verbose="yes";;
    \?)  echo "Usage: $0 -hv -c <config> [start|stop|restart|status]" && exit 1;;
esac
done

# Reset $@
shift `echo $OPTIND-1 | bc`

echo "conf: $conf"
echo "command: $1"

Notes:

Read More

Next:
Previous:
Categories: Technical
Possibly-Related Posts
JavaScript Hack: Hide an Element on a Page

  • http://www.zeuscat.com/andrew/ Andrew Ho

    My standard sh command line parsing loop looks like this:

        while [ $# != 0 ]; do
            flag="$1"
            case "$flag" in
                -a) echo "You provided the -a flag, which takes no arguments"
                    ;;
                -b) if [ $# -gt 1 ]; then
                        arg="$2"
                        shift
                    else
                        echo "You did not provide an argument for the -b flag"
                    fi
                    echo "You supplied an argument for the -b flag: $arg"
                    ;;
                 *) echo "Unrecognized flag or argument: $flag"
                    ;;
            esac
            shift
        done
    

    If you are using bash instead of sh, you can accept both -fvalue and -f value variants:

               -b*) arg="${arg##-b}"
                    echo "You attached an argument to the -b flag: $arg"
                    ;;
    

    My code doesn’t reset $@. I have a README file in my homedir that reminds me of the other neat bash-ism that I like to use: ${1+"$@"} expands to $@, but if $@ is empty, it expands to an empty list (different from “$@” which would expand to the single empty string “”).

  • an_agent

    FAI…

    shift $((OPTIND - 1))

    works in bash (not sh) without having to pipe to bc.

  • Ted Henry

    Thanks for this post! This is exactly what I needed :-)

  • Alexander Brankov

    Hi guys,

    These are very good examples! Thank you to all of you.

    It is comprehensive and easy but here is a different case:

    myscript.sh --first-argument=23 --second-argument="some string" unnamed arguments

    I could not find anything to cook up a good example for this case.

    Your competent help would be much appreciated!

    Many thanks,

    Alex

  • http://dannyman.toldme.com/ dannyman

    Alexander,

    The code in my post handles spaces just fine:

    0-11:45 dannhowa@ops-dev ~> ./test.sh -c 'foo foo' start
    conf: foo foo
    command: start
    
  • http://AlexanderBrankov Alexander Brankov

    Hi Guys,

    It is the equal sign that I am worried about. In this case I can have something like:

    ./my_script.sh –option_1=”Some Text!” –option-2=10

    I struggle to handle this case when I am converting some tools that use this way of calling the commands.

    Kind regards,

    Alex

  • http://AlexanderBrankov Alexander Brankov

    Again the two dashes in front of the arguments names ware merged.