.sh & getopts

Matthew Seaman m.seaman at infracaninophile.co.uk
Sun Jun 6 09:09:32 UTC 2010

Hash: SHA1

On 06/06/2010 08:40:56, Aiza wrote:
> I have been looking for documentation on freebsd's sh shell programming.
> Want to understand what is happening in that getopts I posted. Where can
> I find real explanations?

Well, the essential reference is the sh(1) man page.  Just about
everything you could need to know is in there, but possibly not in a way
that you would find particularly accessible for deconstructing a piece
of arbitrary code.

In this sort of case, probably your best bet is to ask for advice.
While such questions are verging on off-topic for this list they do seem
to come up with reasonable frequency and usually receive pretty good

Let's see... You mean this, I take it:

shift; while getopts :ugr: arg; do case ${arg} in
   u) action="freebsd-update";;
   g) action="freebsd-upgrade";;
   r) action="freebsd-rollback";;
   ?) exerr ${cmd_usage};;
esac; done; shift $(( ${OPTION} -1 ))

Let me reformat that a bit so it's clearer, and add some line numbers:

01: shift
02: while getopts :ugr: arg
03: do
04:     case ${arg} in
05:       u)
06:           action="freebsd-update"
07:           ;;
08:       g)
09:           action="freebsd-upgrade"
10:           ;;
11;       r)
12:           action="freebsd-rollback"
13:           ;;
14:       ?)
15:           exerr ${cmd_usage}
16:           ;;
17:     esac
18: done
19: shift $(( ${OPTION} -1 ))

So, line 01 discards the first item from the argument list -- that's
just the program name.  (The argument list is available as $@ -- which
is somewhat special cased, in that it acts as an array, which is
otherwise not a type provided by posix sh(1))  Actually, I don't think
you need to do this -- getopts is clever enough to deal with that itself.

The while loop from lines 02 to 18 invokes getopts to process the
argument list.  getopts is a sh(1) built-in, but it behaves as if it is
an external program.  It takes two arguments: the list of option
specifiers, and the variable name to set to the current arg being
processed.  Your option string indicates that you expect to deal with

    -r foo
    bar        (ie. a bare argument with no option letter)

(Not actually sure if the bare argument thing actually works.  Usually,
you use getopts to process all of the option flags, then a separate bit
of code to deal with anything else left in $@)

Now, inside the while loop, you just have a case statement so you can
jump to the appropriate code for handling each different option.
Hmmm... except you don't handle the bare argument case, nor do you do
anything with the argument to the '-r' option -- you should copy the
value of $OPTARG somewhere to save for later processing within that part
of the case statement.

Now, the enclosing while loop will process all of the command line
options and as you (re)set the 'action' variable each time, only the
last named of and -u, -g or -r options will be effective.  That's
reasonable given that the available options seem to be mutually
exclusive.  Maybe emitting a warning if you reset the action variable
would be a good idea.  One way of doing that is this construct:

	[ -z ${action} ] || echo "Warning -- action multiply specified"

You can add that between lines 03 and 04.

Finally, at line 19, you attempt to discard presumably all of the
options from $@ that getopts has already processed.  Unfortunately,
$OPTION is (I think) a bash-ism, and not available with the getopts in
FreeBSD sh(1).  Instead, you should be looking at $OPTIND.  Like so:

       shift $(($OPTIND - 1))

All in all, this is pretty much the normal shell code idiom for handling
command line options.  After running this, you should be left
with $@ containing just those arguments /not/ preceded by option flags.



- -- 
Dr Matthew J Seaman MA, D.Phil.                   7 Priory Courtyard
                                                  Flat 3
PGP: http://www.infracaninophile.co.uk/pgpkey     Ramsgate
JID: matthew at infracaninophile.co.uk               Kent, CT11 9PW
Version: GnuPG/MacGPG2 v2.0.14 (Darwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/


More information about the freebsd-questions mailing list