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

Responses

June 22nd, 2005

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

June 24th, 2005

an_agent

FAI…

shift $((OPTIND - 1))

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

January 15th, 2008

Ted Henry

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

July 21st, 2009

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

July 21st, 2009

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
September 4th, 2009

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

September 10th, 2009

Alexander Brankov

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

Comment

Leave a comment . . .

Tiny Print:

  1. For private messages, e-mail me: dannyman@toldme.com.
  2. You must provide an e-mail address.
  3. You can use a bogus e-mail address, but I like to know who you are.
  4. I will not spam you. I will not publish or share your e-mail address.
  5. First-time commenters will be held for review.
  6. You can use these HTML tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>