Web Development

Perl Getopt and GetOptions

Perl Getopt and GetOptions

Two Perl modules (Getopt and Getoptions::Long) work to extract program flags and arguments much like Getopt and Getoptsdo for shell programming. The Perl modules, especially GetOptions::Long, are much more powerful and flexible.

Simple scripts show the power of these:

#!/usr/bin/perl
# script is "./g"
use Getopt::Std;
%options=();
getopts("od:fF",\%options);
# like the shell getopt, "d:" means d takes an argument
print "-o $options{o}\n" if defined $options{o};
print "-d $options{d}\n" if defined $options{d};
print "-f $options{f}\n" if defined $options{f};
print "-F $options{F}\n" if defined $options{F};
print "Unprocessed by Getopt::Std:\n" if $ARGV[0];
foreach (@ARGV) {
  print "$_\n";
}

Trying it out:

bash-2.05a$ ./g  -f -d F -o -F
-o 1
-d F
-f 1
-F 1
bash-2.05a$ ./g -o -d foo
-o 1
-d foo
bash-2.05a$ ./g -o rough -d foo
-o 1
Unprocessed by Getopt::Std:
rough
-d
foo

Processing of arguments stops when it saw “rough”.

If you leave off a required argument, it just gets swallowed:

bash-2.05a$ ./g -d
bash-2.05a$ ./g -d foo
-d foo

But it’s easily confused:

bash-2.05a$ ./g -d -o -f
-d -o
-f 1

It thinks that -o is the argument of -d.

bash-2.05a$ ./g -k
Unknown option: k

Like the simple shell “getopt”, this complains when it gets an option that it doesn’t know about. Unlike the shell “getopt”, prefacing the option string with a “:” doesn’t help. Instead, use “getopt”:

#!/usr/bin/perl
# we'll call this one ./gg
use Getopt::Std;
%options=();
getopt("odfF",\%options);
print "-o $options{o}\n" if defined $options{o};
print "-d $options{d}\n" if defined $options{d};
print "-f $options{f}\n" if defined $options{f};
print "-F $options{F}\n" if defined $options{F};

Note the lack of any “:”. This module doesn’t care which flags take values and which don’t: it assumes ALL of them take arguments.

The “getopt” isn’t very bright:

bash-2.05a$ ./gg -f -o -d foo
-d foo
-f -o
bash-2.05a$ ./g -f -o -d foo
-o 1
-d foo
-f 1

But it doesn’t complain:

bash-2.05a$ ./gg -l
bash-2.05a$ ./g -l
Unknown option: l

Unlike their shell cousins, neither of these have any issues with arguments containing spaces:

bash-2.05a$ ./g -o  -d "foo bar"
-o 1
-d foo bar
bash-2.05a$ ./gg -o "foo"  -d "foo bar"
-o foo
-d foo bar

Far better than either of these is the Getopt::Long module. Here’s a script to play with it:

#!/usr/bin/perl
use Getopt::Long;
GetOptions("o"=>\$oflag,
            "verbose!"=>\$verboseornoverbose,
            "string=s"=>\$stringmandatory,
            "optional:s",\$optionalstring,
            "int=i"=> \$mandatoryinteger,
            "optint:i"=> \$optionalinteger,
            "float=f"=> \$mandatoryfloat,
            "optfloat:f"=> \$optionalfloat);
print "oflag $oflag\n" if $oflag;
print "verboseornoverbose $verboseornoverbose\n" if $verboseornoverbose;
print "stringmandatory $stringmandatory\n" if $stringmandatory;
print "optionalstring $optionalstring\n" if $optionalstring;
print "mandatoryinteger $mandatoryinteger\n" if $mandatoryinteger;
print "optionalinteger $optionalinteger\n" if $optionalinteger;
print "mandatoryfloat $mandatoryfloat\n" if $mandatoryfloat;
print "optionalfloat $optionalfloat\n" if $optionalfloat;

print "Unprocessed by Getopt::Long\n" if $ARGV[0];
foreach (@ARGV) {
  print "$_\n";
}

The hash array that this uses holds the argument name and the type of argument; what that points to is where it will store values for options processed.

Playing with it:

#
# doesn't care if it's -o or --o
bash-2.05a$ ./ggg -o
oflag 1
bash-2.05a$ ./ggg --o
oflag 1
#
# abbreviating is ok too
bash-2.05a$ ./ggg -verbose
verboseornoverbose 1
bash-2.05a$ ./ggg -verb
verboseornoverbose 1
#
# but not this
bash-2.05a$ ./ggg -verbosity
Unknown option: verbosity
#
# $verboseonoverbose will be 0 here
bash-2.05a$ ./ggg -noverb
#
# strings
bash-2.05a$ ./gggg -s
Option string requires an argument
bash-2.05a$ ./ggg -s=foo
stringmandatory foo
bash-2.05a$ ./ggg -optional
bash-2.05a$ ./ggg -optional=foo
optionalstring foo
#
# ambiguity
bash-2.05a$ ./ggg --opt
Option opt is ambiguous (optfloat, optint, optional)
#
# floats and integers
bash-2.05a$ ./ggg --optfloat=75.6
optionalfloat 75.6
bash-2.05a$ ./ggg --optint=75.6
Value "75.6" invalid for option optint (number expected)
bash-2.05a$ ./ggg --optint=75
optionalinteger 75
bash-2.05a$ ./ggg -f --optfloat=75.6 -o
Value "--optfloat=75.6" invalid for option float (real number expected)
oflag 1
# once it runs out of options, it leaves @ARGV alone:
bash-2.05a$ ./ggg -o foo bar
oflag 1

Unprocessed by Getopt::Long
foo
bar

GetOpt::Long is obviously much more flexible. The hash you pass is a little clumsy, but if you think about it, there’s no better way to do it. In some places, you might use something like this:

#!/usr/bin/perl
use Getopt::Long;
my %moo=();
GetOptions("o"=>\$moo{$oflag},
            "verbose!"=>\$moo{verbose},
            "string=s"=>\$moo{stringmandatory},
            "optional:s"=>\$moo{optionalstring},
            "int=i"=> \$moo{mandatoryinteger},
            "optint:i"=> \$moo{optionalinteger},
            "float=f"=> \$moo{mandatoryfloat},
            "optfloat:f"=> \$moo{optionalfloat});

foreach (keys %moo) {
 print "$_ = $moo{$_}\n";
}

print "Unprocessed by Getopt::Long\n" if $ARGV[0];
foreach (@ARGV) {
  print "$_\n";
}

but if you have so many flags that you are thinking that is helpful, your program is surely trying much too hard to be all things to all people.

About the author

Written by Tony Lawrence.

If you found this post useful you may also want to check these out:

  1. Cultured Perl: Reading and Writing Excel Files with Perl
  2. Cultured Perl: Automating UNIX System Administration with Perl
  3. How To Send Email With Perl, Part I
  4. Cultured Perl: Genetic Algorithms Applied with Perl
  5. Using Perl to Create Reusable Web Applications
  6. Zope for the Perl/CGI programmer