Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

GetOpt Organization

by KurtSchwind (Chaplain)
on Mar 09, 2015 at 13:44 UTC ( [id://1119317]=perlquestion: print w/replies, xml ) Need Help??

KurtSchwind has asked for the wisdom of the Perl Monks concerning the following question:

I'm a big fan of using Getopt in my command line programs. What I've become a little dissatisfied with the organization of the flags and variables. I'm looking for some input here from the monks on organizational tips. I'll post a sample of the code style I normally use.

#!/usr/bin/perl use strict; use Getopt::Long; my $showos=0; my $showrel=0; my $showarch=0; my $showiface=0; my $showhd=0; my $showmem=0; my $showuser=0; GetOptions ( "os|o"=>\$showos, "rel|r"=>\$showrel, "arch|a"=>\$showarch, "iface|i"=>\$showiface, "hd|h"=>\$showhd, "mem|m"=>\$showmem, "user|u"=>\$showuser, "all|A"=> sub { $showos = 1; $showrel = 1; $showarch = 1; $sho +wiface = 1; $showhd = 1; $showmem = 1; $showuser = 1; }, "help|h" => sub { &help; exit 0; } ); . . .

Maybe this is already the best pattern to use, but it just looks kind of ugly to me. Any ideas would be appreciated.

--
“For the Present is the point at which time touches eternity.” - CS Lewis

Replies are listed 'Best First'.
Re: GetOpt Organization (or stop)
by tye (Sage) on Mar 09, 2015 at 14:33 UTC

    Leaving the organization question to other respondents, you (and at least one respondent) made a common mistake. GetOptions() can report 1 or more errors via warn() and then return a false value. Because there can be more than 1 error, it doesn't use die(). So it is your responsibility to stop execution for such cases.

    GetOptions( # ... ) or exit 1;

    Or use a die() as shown in Getopt::Long.

    - tye        

Re: GetOpt Organization
by hdb (Monsignor) on Mar 09, 2015 at 14:06 UTC

    Not used this before myself, no idea whether there is an established pattern. Here is my proposal:

    use strict; use warnings; use Getopt::Long; my %parms = ( "os|o" =>\(my $showos =0), "rel|r" =>\(my $showrel =0), "arch|a" =>\(my $showarch =0), "iface|i"=>\(my $showiface=0), "hd|h" =>\(my $showhd =0), "mem|m" =>\(my $showmem =0), "user|u" =>\(my $showuser =0), ); GetOptions ( %parms, "all|A" => sub { $$_=1 for values %parms }, "help|h" => sub { &help; exit 0; } ); $showos=5; print "$showos,$showrel,$showarch,$showiface,$showhd,$showmem,$showuse +r\n";

      Minor bug - The first param to GetOptions needs to be a reference to a hash \%parms

        That may be so but the code seems to work anyway.

        UPDATE: ...reason being that there are several ways to use GetOpt::Long, one of which does not require a hash ref as first parameter.

Re: GetOpt Organization
by karlgoethebier (Abbot) on Mar 09, 2015 at 14:52 UTC

    My default - less or more:

    use Getopt::Long; use Pod::Usage; # IMHO a must Getopt::Long::Configure("no_ignore_case"); # double your options ;-) GetOptions(\%options, "help", "foo=s","bar=s",); # or what ever pod2usage( -exitstatus => 0, -verbose => 2 ) if $options{help}; foreach my $option (%options ) { pod2usage( -exitstatus => 2, -verbose => 1 ) unless $option; } __END__ # POD section here

    Something like this is also very handy (should be self explaining):

    GetOptions( \%options, "help", "suffix:s", "amount=i", "dir=s", "logpath=s", " +From=s", "Subject:s", "Data:s", "Host:s", "patterns=s{1,}" => \@patterns, "To=s{1,}" => \@recipients ); # stuff __END__ ./foo.pl -a 2 \ -d /path/to/dir \ -l /path/to/logs \ -F user@domain -s suffix \ -p foo bar \ -T user@domain other@domain

    Hint: If you declare "foo", you can call it as -f as well as --foo.

    Regards, Karl

    «The Crux of the Biscuit is the Apostrophe»

      tsk, tsk.. no monk checks for the return status of GetOptions ?? You want your program running with invalid option passed?

      Here is my usual pattern:
      use Getopt::Long; use Pod::Usage; unless ( GetOptions ( "color=s"=>\$par_color, "help" => \$par_help, ) ) {pod2usage(-verbose => 1,-exitval => 'NOEXIT'); &wait_for_in +put; exit;} if (defined $par_help){pod2usage(-verbose => 1,-exitval => 'NOEXIT'); +&wait_for_input; exit;}
      The function pod2usage supports -exitval => 'NOEXIT' (ie normally it exits the program unless you specify it to not do) useful if you started the program without a console, like in a Tk application as Tk Tartaglia's triangle fun - Pascal's triangle fun.

      HtH
      L*
      There are no rules, there are no thumbs..
      Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.

        tsk, tsk.. no monk checks for the return status of GetOptions ?? You want your program running with invalid option passed?

        The validation options and return value of GetOptions , and esp their combination, isn't that useful

        You still have to do your own validation, so let GetOptions warn, then write your program

        ...You want your program running with invalid option passed?

        No, perish the thought.

        When i run the snippet i posted with an invalid option i get:

        monks>options.pl --goo Unknown option: goo

        Best regards, Karl

        «The Crux of the Biscuit is the Apostrophe»

Re: GetOpt Organization
by atcroft (Abbot) on Mar 09, 2015 at 14:03 UTC

    Personally, I try to organize in a way that makes sense and is relatively easy to read. To me, that would mean putting the simple options in some order (either by type or, more likely for me, in alphabetical order), then putting more complex items (such as your "all" option) lower (also ordered), with the "help" option (if present) at the end. (I would also probably have the help() sub close by, so it would be easy to remember to update if an option were added/removed/changed.) I might also consider storing those configuration options in a hash, so as to make passing them easier.

    Hope that helps, and I look forward to the responses you get on this thread for the possibility of ideas to consider as well.

Re: GetOpt Organization
by choroba (Cardinal) on Mar 09, 2015 at 14:20 UTC
    If the number of options is higher (i.e. more than 4, YMMV), I'd use a hash:
    my %opt; GetOtions(\%opt, qw( os|o rel|r arch|a iface|i hd|h mem|m ));

    See "Storing options values in a hash" in Getopt::Long for details.

    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ

      Whenever I touch Getopt::Long-using code that uses a hash, I usually change it to something that uses variables. In many of those cases, there are actually places in the code using an option name that isn't one of the ones populated by the call. "use strict" prevents such mistakes when variables are used.

      I also prefer to reduce the scope of many of the options. Instead of making the hash available "everywhere", I can often make it clearer which options impact the behavior of a particular sub. Or that some option(s) impact nothing other than some small scope.

      - tye        

        "I also prefer to reduce the scope of many of the options. Instead of making the hash available "everywhere", I can often make it clearer which options impact the behavior of a particular sub. Or that some option(s) impact nothing other than some small scope."

        Fascinating.

        But to be honest, i'm a bit unsure if i guessed right what you mean.

        Or in other words: IMHO there is nothing wrong with this idiom:

        MAIN: { my %options; GetOptions(\%options, "foo", "bar"); nose ($options{foo}, $options{bar}); } sub nose { my ($foo, $bar) = @_; # stuff my $cuke; }

        N.B.: First time post of untested code.

        Thank you very much for advice and best regards, Karl

        «The Crux of the Biscuit is the Apostrophe»

        Excuse my slow wit, but I can't see how a lexical scalar is less "everywhere" than a lexical hash? How do you reduce the scope of options?
        لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1119317]
Approved by hdb
Front-paged by Arunbear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (4)
As of 2024-04-19 17:15 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found