Perl Advent Calendar 2006-12-12

Santa makes a list, and checks it twice… but he only makes one list.

by Jeff Lavallee

How many times have you seen (or written) a perl script that was well documented with POD, but also had a usage() function which duplicated some of the information contained in the POD? Every time you add an option, you need to remember to document it in both. What if you want to display a detailed description of the options to the user? Things only get messier. Pod::Usage

Pod::Usage allows you to make portions of your POD available to the user as help. Pod::Usage provides the pod2usage subroutine, which accepts a number of options. The most commonly needed are -message (the initial line of the usage message e.g; brief description of the program) and -verbose (control how detailed the help output is). -exitval can be used to control the exit value returned to the shell, most commonly to exit with a non-zero status if invalid arguments were provided.

Using Pod::Usage to handle our usage information for us, we've managed to completely prevent any documentation duplication (say that three times fast) in our sample code. Let's take a look at in action:

santa@northpole:~/ $ ./mod12.pl 
 --filename required
Usage:
     my_script.pl [-v] -f <filename>

Options:
    -f or --file <filename> (required)
            The filename to be used as input. Required.

    -h or --help
            Print a brief help message and exits.

    -v or --verbose
            Print verbose output as the script runs. Can be used more than
            once to increase the verbosity.

Oops, we didn't supply a required parameter. pod2usage helpfully prints the script's usage information along with the error. Of course, had we checked the syntax the first time --help would have given us briefer but clear directions:

santa@northpole:~/ $ ./mod12.pl --help
Usage:
     my_script.pl [-v] -f <filename>

If we'd really wanted the verbose help we could have gotten that too:

santa@northpole:~/ $ ./mod12.pl --help --verbose
Usage:
     my_script.pl [-v] -f <filename>

Options:
    -f or --file <filename> (required)
            The filename to be used as input. Required.

    -h or --help
            Print a brief help message and exits.

    -v or --verbose
            Print verbose output as the script runs. Can be used more than
            once to increase the verbosity.

Using the verbose option more than once displays the full man page (probably better provided with a --man option):

santa@workshop:~ $ ./mod12.pl --help -v -v
MOD12(1)              User Contributed Perl Documentation             MOD12(1)
NAME
my_script.pl SYNOPSIS my_script.pl [-v] -f <filename> OPTIONS -f or --file <filename> (required) The filename to be used as input. Required. -h or --help Print a brief help message and exits. -v or --verbose Print verbose output as the script runs. Can be used more than once to increase the verbosity. BUGS None known at this time perl v5.8.6 2006-12-12 MOD12(1)

Our POD can now be used to display help for our script based on the command line options provided, and all the documentation is now in one place. We do still have to keep the documentation in sync with the call to GetOptions, however. Is there a way we can prevent even that duplication? Yes.

mod12.pl


  
  1 #!/usr/bin/perl
  2
  3 =head1 NAME 
  4 
  5 my_script.pl
  6 
  7 =head1 SYNOPSIS
  8 
  9  my_script.pl [-v] -f 
 10 
 11 =head1 OPTIONS
 12 
 13 =over 8
 14 
 15 =item B<-f> or B<--file>  (required)
 16 
 17 The filename to be used as input.  Required.
 18 
 19 =item B<-h> or B<--help>
 20 
 21 Print a brief help message and exits.
 22 
 23 =item B<-v> or B<--verbose>
 24 
 25 Print verbose output as the script runs. 
 26 Can be used more than once to increase the verbosity.
 27 
 28 =back
 29 
 30 =head1 BUGS
 31 
 32 None known at this time
 33 
 34 =cut
 35
 36 use Pod::Usage qw/ pod2usage /;
 37 use Getopt::Long qw/ GetOptions /;
 38 
 39 GetOptions( 'file=s'   => \(my $file),
 40             'verbose+' => \(my $verbose),
 41             'help'     => \(my $help),
 42           );
 43 
 44 pod2usage(-verbose => $verbose) if $help;
 45 pod2usage( -message => '--filename required',
 46            -exitval => 1,
 47            -verbose => $verbose,
 48 ) unless defined $file;
 49 
 50 
 51 
 52 print "I got the following options:
 53 file:    $file
 54 verbose: $verbose
 55 " if $verbose;