david.shin has asked for the wisdom of the Perl Monks concerning the following question:


I want to factor out the GetOptions(...) call into a library but have run into a few issues.
So here's what I want to do:

Main Program:
- create a hash
- create an array of parameters I want to read off the command line in the GetOptions(...) format (ie "home=s", "instance=s", fileSuffix:s", etc)
- pass a reference to the hash and array to the Util method encapsulating the GetOptions call

Util method w/GetOptions call:
Now this is where the problem is, I don't know how to format a string so it can fill in the hash.
I tried iterating over each entry in the array and calling GetOptions(...) individually, but on the first call, it tries to load up all the paramters and then on subsequent calls cannot find any paramters (since they've been consumed by the first call) (if this is wrong, please correct me as well).

So then I found another method where you can pass in a hash ref and the parameter you want.
- ie GetOptions( \%myHash, "home=s");

So I tried to pass in one huge string consisting of all the parameters I want
( ie GetOptions( \%myHash, "home=s,instance=s,fileSuffx:s") but that didn't work either (which didn't surprise me).

Any suggestions out there? Thanks a lot!!

Replies are listed 'Best First'.
Re: Getopt::Long
by BazB (Priest) on May 28, 2004 at 21:25 UTC

    Why bother? Getopt::Long is already nicely simplified - you're just making things more complicated again.

    You're always going to have to establish what command line arguments you need to accept, what type they are (required/optional strings, numbers, switches etc) and populate variables or data structures. This is likely to be different for each program and so doesn't warrant another layer of code IMHO.

    If the information in this post is inaccurate, or just plain wrong, don't just downvote - please post explaining what's wrong.
    That way everyone learns.

      This library is responsible for ~25 interfaces and we're in the process of refactoring. So, refactoring out as much as possible just makes it easier in the long run.
Re: Getopt::Long
by etcshadow (Priest) on May 29, 2004 at 02:12 UTC
    So I tried to pass in one huge string consisting of all the parameters I want ( ie GetOptions( \%myHash, "home=s,instance=s,fileSuffx:s") but that didn't work either (which didn't surprise me).
    I think, actually, that all you're missing is that the parameter descriptions must be an array (not just a big string run together with commas). Try this:
    GetOptions( \%myHash, "home=s", "instance=s", "fileSuffx=s")
    ------------ :Wq Not an editor command: Wq
      Thanks a lot!! This is exactly what I needed, although I think I should of figured that out myself (<== perl noob). Live 'n learn.
Re: Getopt::Long
by Anonymous Monk on May 28, 2004 at 22:54 UTC

    i think this is close to what you want. it's not complete or nice, just a test of doablility.

    alot of my scripts share common command line args that are a pain to maintain with cut and paste.

    • everything gets a --help and --man that reads pod from the current file or from a specific pod.
    • almost everything works against a list of hosts. hosts can be in files given by --file or specified individually by --host or any combination. even --host foo,blah,bling --list bam.lst,wiz.lst --host thistoo.
    • most things require passwords which can usually be fetched from a database, but in the worst case scenerios there needs to be a way to specify all needed passwords by options, --password --password --password cisco.console=foo.
    and those are just the basics, some things need database options, some need accounting options, some need more options than you can shake a stick at.

    so i started to wrap them up into modules. you probably only need to check the docs for the pass_through option.

    package DNO::Options; use strict; use Exporter (); use vars qw( @ISA @EXPORT @EXPORT_OK ); @ISA = qw( Exporter ); @EXPORT = qw( get_options ); @EXPORT_OK = qw( uncommify ); use Getopt::Long qw( :config pass_through ); sub uncommify { my $aref = shift; return split /,/, join ',', @$aref; } sub get_options { my $def = shift; my $cur = {}; $cur->{$_->[0]} = $_->[2] for @$def; my @def = map {$_->[0].$_->[1]} @$def; GetOptions( $cur, @def ); return $cur; } 1; package DNO::Passwords; use strict; use vars qw( @ISA @EXPORT ); @ISA = qw( Exporter ); @EXPORT = qw( passwords ); use DNO::Options qw( get_options ); use DNO::Documentation qw( usage ); my ($def, $opt); $def = [ [ 'password', '=s%', {} ], ]; sub passwords { my ( @want ) = @_; $opt = get_options( $def ); my %pw = map { $_ => undef } @want; $pw{$_} = $opt->{password}{$_} for keys %{ $opt->{password} }; my @need = grep { not defined $pw{$_} } keys %pw; if ( @need ) { require DNO::Util; DNO::Util->import( qw/ get_passwords / ); my $pw = get_passwords( @need ); unless ( defined $pw ) { usage( -verbose => 1, -message => "$0 +: no passwords. did you kinit?$/" ); } $pw{$_} = $pw->{$_} for @need; @need = grep { not defined $pw{$_} } keys %pw; } if ( @need ) { usage( -verbose => 1, -message => "$0: missing pass +words: @need$/" ); } return \%pw; } 1; package DNO::Devices; use strict; use vars qw( @ISA @EXPORT @EXPORT_OK ); @ISA = qw( Exporter ); @EXPORT = qw( devices ); @EXPORT_OK = qw( ); use DNO::Options qw( get_options uncommify ); use DNO::Documentation qw( usage ); my ($def, $opt); my @hosts; $def = [ [ 'host', '=s@', [] ], [ 'list', '=s@', [] ], ]; sub devices { $opt = get_options( $def ); foreach my $file ( uncommify($opt->{list}) ) { local *F; open F, '<', $file or usage( -verbose => 1, -message => "$0: open $file: $!$/") +; while ( <F> ) { s/#.*//; next if /^\s*$/; push @hosts, (split)[0]; } close F; } push @hosts, uncommify($opt->{host}); #unless ( @hosts ) { usage( -verbose => 1, -message => "$0: no dev +ices to work with.$/" ); } return @hosts; } 1; package DNO::Documentation; use strict; # Globals use vars qw( @ISA @EXPORT @EXPORT_OK %INC ); # Exports @ISA = qw( Exporter ); @EXPORT = qw( usage ); @EXPORT_OK = qw( doc_setup ); # Use use Pod::Usage; use DNO::Options qw( get_options ); # Options definitions my ($def, $opt); $def = [ [ 'help', '', 0 ], [ 'man', '', 0 ], ]; # Class Data my %Default = ( ); # Find our documentation directory and add to searchpath my $class = __PACKAGE__ . '.pm'; $class =~ s/::/\//g; my $path = $INC{$class}; $path =~ s/\.pm$//; $Default{'-pathlist'} = "$path:$ENV{PATH}"; 1;

    and a script that uses the modules can now look like this.

    #!/usr/bin/perl use strict; use warnings; $|++; #use Data::Dumper; use lib '../lib'; use DNO::Documentation qw( doc_setup usage ); doc_setup(); use DNO::Passwords qw( passwords ); my $pw = passwords( qw/ / ); #print Dumper($pw); use DNO::Devices qw( devices ); my @hosts = devices(); #print Dumper(\@hosts); use DNO::Options qw( get_options ); my ($def, $opt); $def = [ [ 'maxfork', '=i', 20 ], [ 'timeout', '=i', 2 ], [ 'retries', '=i', 2 ], ]; $opt = get_options( $def ); # ... do something __END__ $ dosumthin --host mary --list domain1,domain3 --password +ic --timeout 5 --retries 4

    there are probably better ways to accomplish the same thing. but it is nice that i only have to have a few ugly lines in each script to handle the docs, the hosts, the passwords, ... instead of a huge getopts in each script.

      Thanks for the code snippet, but simpler is better for now.